/*
Copyright (C) Paul Falstad and Iain Sharp
This file is part of CircuitJS1.
CircuitJS1 is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
CircuitJS1 is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with CircuitJS1. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lushprojects.circuitjs1.client;
// GWT conversion (c) 2015 by Iain Sharp
// For information about the theory behind this, see Electronic Circuit & System Simulation Methods by Pillage
import java.util.Vector;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.lang.Math;
import com.google.gwt.canvas.client.Canvas;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.CellPanel;
import com.google.gwt.user.client.ui.DockLayoutPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.RootLayoutPanel;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.canvas.dom.client.Context2d;
import com.google.gwt.canvas.dom.client.Context2d.LineCap;
import com.google.gwt.event.dom.client.MouseDownEvent;
import com.google.gwt.event.dom.client.MouseDownHandler;
import com.google.gwt.event.dom.client.MouseEvent;
import com.google.gwt.event.dom.client.MouseMoveEvent;
import com.google.gwt.event.dom.client.MouseMoveHandler;
import com.google.gwt.event.dom.client.MouseUpHandler;
import com.google.gwt.event.dom.client.MouseUpEvent;
import com.google.gwt.event.dom.client.MouseOutEvent;
import com.google.gwt.event.dom.client.MouseOutHandler;
import com.google.gwt.event.dom.client.ContextMenuEvent;
import com.google.gwt.event.dom.client.ContextMenuHandler;
import com.google.gwt.user.client.Event.NativePreviewEvent;
import com.google.gwt.user.client.Event.NativePreviewHandler;
import com.google.gwt.event.dom.client.MouseWheelEvent;
import com.google.gwt.event.dom.client.MouseWheelHandler;
import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.http.client.Request;
import com.google.gwt.http.client.RequestException;
import com.google.gwt.http.client.Response;
import com.google.gwt.http.client.RequestBuilder;
import com.google.gwt.http.client.RequestCallback;
import com.google.gwt.user.client.ui.MenuBar;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.DoubleClickHandler;
import com.google.gwt.event.dom.client.DoubleClickEvent;
import com.google.gwt.dom.client.CanvasElement;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.user.client.ui.MenuItem;
import com.google.gwt.safehtml.shared.SafeHtml;
import com.google.gwt.safehtml.shared.SafeHtmlUtils;
import com.google.gwt.storage.client.Storage;
import com.google.gwt.user.client.ui.PopupPanel;
import static com.google.gwt.event.dom.client.KeyCodes.*;
import com.google.gwt.http.client.URL;
import com.google.gwt.user.client.Window.Location;
import com.google.gwt.user.client.ui.Frame;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.user.client.Window.Navigator;
public class CirSim implements MouseDownHandler, MouseMoveHandler, MouseUpHandler,
ClickHandler, DoubleClickHandler, ContextMenuHandler, NativePreviewHandler,
MouseOutHandler, MouseWheelHandler {
Random random;
public static final int sourceRadius = 7;
public static final double freqMult = 3.14159265*2*4;
// IES - remove interaction
Button resetButton;
Button runStopButton;
Button dumpMatrixButton;
MenuItem aboutItem;
MenuItem importFromLocalFileItem, importFromTextItem,
exportAsUrlItem, exportAsLocalFileItem, exportAsTextItem;
MenuItem importFromDropboxItem;
MenuItem exportToDropboxItem;
MenuItem undoItem, redoItem,
cutItem, copyItem, pasteItem, selectAllItem, optionsItem;
MenuBar optionsMenuBar;
CheckboxMenuItem dotsCheckItem;
CheckboxMenuItem voltsCheckItem;
CheckboxMenuItem powerCheckItem;
CheckboxMenuItem smallGridCheckItem;
CheckboxMenuItem crossHairCheckItem;
CheckboxMenuItem showValuesCheckItem;
CheckboxMenuItem conductanceCheckItem;
CheckboxMenuItem euroResistorCheckItem;
CheckboxMenuItem printableCheckItem;
CheckboxMenuItem conventionCheckItem;
private Label powerLabel;
private Label titleLabel;
private Scrollbar speedBar;
private Scrollbar currentBar;
private Scrollbar powerBar;
MenuBar elmMenuBar;
MenuItem elmEditMenuItem;
MenuItem elmCutMenuItem;
MenuItem elmCopyMenuItem;
MenuItem elmDeleteMenuItem;
MenuItem elmScopeMenuItem;
MenuItem elmFlipMenuItem;
MenuBar scopeMenuBar;
MenuBar transScopeMenuBar;
MenuBar mainMenuBar;
CheckboxMenuItem scopeVMenuItem;
CheckboxMenuItem scopeIMenuItem;
CheckboxMenuItem scopeScaleMenuItem;
CheckboxMenuItem scopeMaxMenuItem;
CheckboxMenuItem scopeMinMenuItem;
CheckboxMenuItem scopeFreqMenuItem;
CheckboxMenuItem scopeFFTMenuItem;
CheckboxMenuItem scopeRMSMenuItem;
CheckboxMenuItem scopePowerMenuItem;
CheckboxMenuItem scopeIbMenuItem;
CheckboxMenuItem scopeIcMenuItem;
CheckboxMenuItem scopeIeMenuItem;
CheckboxMenuItem scopeVbeMenuItem;
CheckboxMenuItem scopeVbcMenuItem;
CheckboxMenuItem scopeVceMenuItem;
CheckboxMenuItem scopeVIMenuItem;
CheckboxMenuItem scopeXYMenuItem;
CheckboxMenuItem scopeResistMenuItem;
CheckboxMenuItem scopeVceIcMenuItem;
CheckboxMenuItem scopeMaxScaleMenuItem;
CheckboxMenuItem scopeMaxScaleTransMenuItem;
MenuItem scopeRemovePlotMenuItem;
MenuItem scopeSelectYMenuItem;
static HashMap<String,String> localizationMap;
String lastCursorStyle;
boolean mouseWasOverSplitter = false;
// Class addingClass;
PopupPanel contextPanel = null;
int mouseMode = MODE_SELECT;
int tempMouseMode = MODE_SELECT;
String mouseModeStr = "Select";
static final double pi = 3.14159265358979323846;
static final int MODE_ADD_ELM = 0;
static final int MODE_DRAG_ALL = 1;
static final int MODE_DRAG_ROW = 2;
static final int MODE_DRAG_COLUMN = 3;
static final int MODE_DRAG_SELECTED = 4;
static final int MODE_DRAG_POST = 5;
static final int MODE_SELECT = 6;
static final int MODE_DRAG_SPLITTER = 7;
static final int infoWidth = 120;
long myframes =1;
long mytime=0;
long myruntime=0;
long mydrawtime=0;
int dragGridX, dragGridY, dragScreenX, dragScreenY, initDragGridX, initDragGridY;
long mouseDownTime;
long zoomTime;
int mouseCursorX = -1;
int mouseCursorY = -1;
int selectedSource;
Rectangle selectedArea;
int gridSize, gridMask, gridRound;
boolean dragging;
boolean analyzeFlag;
boolean dumpMatrix;
// boolean useBufferedImage;
boolean isMac;
String ctrlMetaKey;
double t;
int pause = 10;
int scopeSelected = -1;
int menuScope = -1;
int menuPlot = -1;
int hintType = -1, hintItem1, hintItem2;
String stopMessage;
double timeStep;
static final int HINT_LC = 1;
static final int HINT_RC = 2;
static final int HINT_3DB_C = 3;
static final int HINT_TWINT = 4;
static final int HINT_3DB_L = 5;
Vector<CircuitElm> elmList;
// Vector setupList;
CircuitElm dragElm, menuElm, stopElm;
private CircuitElm mouseElm=null;
boolean didSwitch = false;
int mousePost = -1;
CircuitElm plotXElm, plotYElm;
int draggingPost;
SwitchElm heldSwitchElm;
double circuitMatrix[][], circuitRightSide[],
origRightSide[], origMatrix[][];
RowInfo circuitRowInfo[];
int circuitPermute[];
boolean simRunning;
boolean circuitNonLinear;
int voltageSourceCount;
int circuitMatrixSize, circuitMatrixFullSize;
boolean circuitNeedsMap;
// public boolean useFrame;
int scopeCount;
Scope scopes[];
boolean showResistanceInVoltageSources;
int scopeColCount[];
static EditDialog editDialog, customLogicEditDialog;
static ExportAsUrlDialog exportAsUrlDialog;
static ExportAsTextDialog exportAsTextDialog;
static ExportAsLocalFileDialog exportAsLocalFileDialog;
static ImportFromTextDialog importFromTextDialog;
static ImportFromDropbox importFromDropbox;
static ExportToDropbox exportToDropbox;
static ScrollValuePopup scrollValuePopup;
static AboutBox aboutBox;
static ImportFromDropboxDialog importFromDropboxDialog;
// Class dumpTypes[], shortcuts[];
String shortcuts[];
static String muString = "\u03bc";
static String ohmString = "\u03a9";
String clipboard;
Rectangle circuitArea;
Vector<String> undoStack, redoStack;
double transform[];
DockLayoutPanel layoutPanel;
MenuBar menuBar;
MenuBar fileMenuBar;
VerticalPanel verticalPanel;
CellPanel buttonPanel;
private boolean mouseDragging;
double scopeHeightFraction=0.2;
Vector<CheckboxMenuItem> mainMenuItems = new Vector<CheckboxMenuItem>();
Vector<String> mainMenuItemNames = new Vector<String>();
LoadFile loadFileInput;
Frame iFrame;
Canvas cv;
Context2d cvcontext;
Canvas backcv;
Context2d backcontext;
static final int MENUBARHEIGHT=30;
static int VERTICALPANELWIDTH=166; // default
static final int POSTGRABSQ=25;
static final int MINPOSTGRABSIZE = 256;
final Timer timer = new Timer() {
public void run() {
updateCircuit();
}
};
final int FASTTIMER=16;
int getrand(int x) {
int q = random.nextInt();
if (q < 0)
q = -q;
return q % x;
}
public void setCanvasSize(){
int width, height;
width=(int)RootLayoutPanel.get().getOffsetWidth();
height=(int)RootLayoutPanel.get().getOffsetHeight();
height=height-MENUBARHEIGHT;
width=width-VERTICALPANELWIDTH;
if (cv != null) {
cv.setWidth(width + "PX");
cv.setHeight(height + "PX");
cv.setCoordinateSpaceWidth(width);
cv.setCoordinateSpaceHeight(height);
}
if (backcv != null) {
backcv.setWidth(width + "PX");
backcv.setHeight(height + "PX");
backcv.setCoordinateSpaceWidth(width);
backcv.setCoordinateSpaceHeight(height);
}
setCircuitArea();
}
void setCircuitArea() {
int height = cv.getCanvasElement().getHeight();
int width = cv.getCanvasElement().getWidth();
int h = (int) ((double)height * scopeHeightFraction);
/*if (h < 128 && winSize.height > 300)
h = 128;*/
circuitArea = new Rectangle(0, 0, width, height-h);
}
// Circuit applet;
CirSim() {
// super("Circuit Simulator v1.6d");
// applet = a;
// useFrame = false;
theSim = this;
}
String startCircuit = null;
String startLabel = null;
String startCircuitText = null;
String startCircuitLink = null;
// String baseURL = "http://www.falstad.com/circuit/";
public void init() {
boolean printable = false;
boolean convention = true;
boolean euroRes = false;
boolean usRes = false;
MenuBar m;
CircuitElm.initClass(this);
QueryParameters qp = new QueryParameters();
try {
//baseURL = applet.getDocumentBase().getFile();
// look for circuit embedded in URL
// String doc = applet.getDocumentBase().toString();
String cct=qp.getValue("cct");
if (cct!=null)
startCircuitText = cct.replace("%24", "$");
startCircuit = qp.getValue("startCircuit");
startLabel = qp.getValue("startLabel");
startCircuitLink = qp.getValue("startCircuitLink");
euroRes = qp.getBooleanValue("euroResistors", false);
usRes = qp.getBooleanValue("usResistors", false);
printable = qp.getBooleanValue("whiteBackground", getOptionFromStorage("whiteBackground", false));
convention = qp.getBooleanValue("conventionalCurrent",
getOptionFromStorage("conventionalCurrent", true));
} catch (Exception e) { }
boolean euroSetting = false;
if (euroRes)
euroSetting = true;
else if (usRes)
euroSetting = false;
else
euroSetting = getOptionFromStorage("euroResistors", !weAreInUS());
transform = new double[6];
String os = Navigator.getPlatform();
isMac = (os.toLowerCase().contains("mac"));
ctrlMetaKey = (isMac) ? "Cmd" : "Ctrl";
shortcuts = new String[127];
layoutPanel = new DockLayoutPanel(Unit.PX);
fileMenuBar = new MenuBar(true);
importFromLocalFileItem = new MenuItem(LS("Import From Local File"), new MyCommand("file","importfromlocalfile"));
importFromLocalFileItem.setEnabled(LoadFile.isSupported());
fileMenuBar.addItem(importFromLocalFileItem);
importFromTextItem = new MenuItem(LS("Import From Text"), new MyCommand("file","importfromtext"));
fileMenuBar.addItem(importFromTextItem);
importFromDropboxItem = new MenuItem(LS("Import From Dropbox"), new MyCommand("file", "importfromdropbox"));
fileMenuBar.addItem(importFromDropboxItem);
exportAsUrlItem = new MenuItem(LS("Export As Link"), new MyCommand("file","exportasurl"));
fileMenuBar.addItem(exportAsUrlItem);
exportAsLocalFileItem = new MenuItem(LS("Export As Local File"), new MyCommand("file","exportaslocalfile"));
exportAsLocalFileItem.setEnabled(ExportAsLocalFileDialog.downloadIsSupported());
fileMenuBar.addItem(exportAsLocalFileItem);
exportAsTextItem = new MenuItem(LS("Export As Text"), new MyCommand("file","exportastext"));
fileMenuBar.addItem(exportAsTextItem);
exportToDropboxItem = new MenuItem(LS("Export To Dropbox"), new MyCommand("file", "exporttodropbox"));
exportToDropboxItem.setEnabled(ExportToDropbox.isSupported());
fileMenuBar.addItem(exportToDropboxItem);
fileMenuBar.addSeparator();
aboutItem=new MenuItem(LS("About"),(Command)null);
fileMenuBar.addItem(aboutItem);
aboutItem.setScheduledCommand(new MyCommand("file","about"));
int width=(int)RootLayoutPanel.get().getOffsetWidth();
VERTICALPANELWIDTH = width/5;
if (VERTICALPANELWIDTH > 166)
VERTICALPANELWIDTH = 166;
if (VERTICALPANELWIDTH < 128)
VERTICALPANELWIDTH = 128;
menuBar = new MenuBar();
menuBar.addItem(LS("File"), fileMenuBar);
verticalPanel=new VerticalPanel();
// make buttons side by side if there's room
buttonPanel=(VERTICALPANELWIDTH == 166) ? new HorizontalPanel() : new VerticalPanel();
m = new MenuBar(true);
m.addItem(undoItem = menuItemWithShortcut(LS("Undo"), LS("Ctrl-Z"), new MyCommand("edit","undo")));
m.addItem(redoItem = menuItemWithShortcut(LS("Redo"), LS("Ctrl-Y"), new MyCommand("edit","redo")));
m.addSeparator();
m.addItem(cutItem = menuItemWithShortcut(LS("Cut"), LS("Ctrl-X"), new MyCommand("edit","cut")));
m.addItem(copyItem = menuItemWithShortcut(LS("Copy"), LS("Ctrl-C"), new MyCommand("edit","copy")));
m.addItem(pasteItem = menuItemWithShortcut(LS("Paste"), LS("Ctrl-V"), new MyCommand("edit","paste")));
pasteItem.setEnabled(false);
m.addItem(menuItemWithShortcut(LS("Duplicate"), LS("Ctrl-D"), new MyCommand("edit","duplicate")));
m.addSeparator();
m.addItem(selectAllItem = menuItemWithShortcut(LS("Select All"), LS("Ctrl-A"), new MyCommand("edit","selectAll")));
m.addSeparator();
m.addItem(new MenuItem(weAreInUS() ? LS("Center Circuit") : LS("Centre Circuit"), new MyCommand("edit", "centrecircuit")));
m.addItem(menuItemWithShortcut(LS("Zoom 100%"), "0", new MyCommand("edit", "zoom100")));
m.addItem(menuItemWithShortcut(LS("Zoom In"), "+", new MyCommand("edit", "zoomin")));
m.addItem(menuItemWithShortcut(LS("Zoom Out"), "-", new MyCommand("edit", "zoomout")));
menuBar.addItem(LS("Edit"),m);
MenuBar drawMenuBar = new MenuBar(true);
drawMenuBar.setAutoOpen(true);
menuBar.addItem(LS("Draw"), drawMenuBar);
m = new MenuBar(true);
m.addItem(new MenuItem(LS("Stack All"), new MyCommand("scopes", "stackAll")));
m.addItem(new MenuItem(LS("Unstack All"),new MyCommand("scopes", "unstackAll")));
m.addItem(new MenuItem(LS("Combine All"),new MyCommand("scopes", "combineAll")));
menuBar.addItem(LS("Scopes"), m);
optionsMenuBar = m = new MenuBar(true );
menuBar.addItem(LS("Options"), optionsMenuBar);
m.addItem(dotsCheckItem = new CheckboxMenuItem(LS("Show Current")));
dotsCheckItem.setState(true);
m.addItem(voltsCheckItem = new CheckboxMenuItem(LS("Show Voltage"),
new Command() { public void execute(){
if (voltsCheckItem.getState())
powerCheckItem.setState(false);
setPowerBarEnable();
}
}));
voltsCheckItem.setState(true);
m.addItem(powerCheckItem = new CheckboxMenuItem(LS("Show Power"),
new Command() { public void execute(){
if (powerCheckItem.getState())
voltsCheckItem.setState(false);
setPowerBarEnable();
}
}));
m.addItem(showValuesCheckItem = new CheckboxMenuItem(LS("Show Values")));
showValuesCheckItem.setState(true);
//m.add(conductanceCheckItem = getCheckItem(LS("Show Conductance")));
m.addItem(smallGridCheckItem = new CheckboxMenuItem(LS("Small Grid"),
new Command() { public void execute(){
setGrid();
}
}));
m.addItem(crossHairCheckItem = new CheckboxMenuItem(LS("Show Cursor Cross Hairs"),
new Command() { public void execute(){
setOptionInStorage("crossHair", crossHairCheckItem.getState());
}
}));
crossHairCheckItem.setState(getOptionFromStorage("crossHair", false));
m.addItem(euroResistorCheckItem = new CheckboxMenuItem(LS("European Resistors"),
new Command() { public void execute(){
setOptionInStorage("euroResistors", euroResistorCheckItem.getState());
}
}));
euroResistorCheckItem.setState(euroSetting);
m.addItem(printableCheckItem = new CheckboxMenuItem(LS("White Background"),
new Command() { public void execute(){
int i;
for (i=0;i<scopeCount;i++)
scopes[i].setRect(scopes[i].rect);
setOptionInStorage("whiteBackground", printableCheckItem.getState());
}
}));
printableCheckItem.setState(printable);
m.addItem(conventionCheckItem = new CheckboxMenuItem(LS("Conventional Current Motion"),
new Command() { public void execute(){
setOptionInStorage("conventionalCurrent", conventionCheckItem.getState());
}
}));
conventionCheckItem.setState(convention);
m.addItem(optionsItem = new CheckboxAlignedMenuItem(LS("Other Options..."),
new MyCommand("options","other")));
mainMenuBar = new MenuBar(true);
mainMenuBar.setAutoOpen(true);
composeMainMenu(mainMenuBar);
composeMainMenu(drawMenuBar);
layoutPanel.addNorth(menuBar, MENUBARHEIGHT);
layoutPanel.addEast(verticalPanel, VERTICALPANELWIDTH);
RootLayoutPanel.get().add(layoutPanel);
cv =Canvas.createIfSupported();
if (cv==null) {
RootPanel.get().add(new Label("Not working. You need a browser that supports the CANVAS element."));
return;
}
cvcontext=cv.getContext2d();
backcv=Canvas.createIfSupported();
backcontext=backcv.getContext2d();
setCanvasSize();
layoutPanel.add(cv);
verticalPanel.add(buttonPanel);
buttonPanel.add(resetButton = new Button(LS("Reset")));
resetButton.addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
resetAction();
}
});
resetButton.setStylePrimaryName("topButton");
buttonPanel.add(runStopButton = new Button(LSHTML("<Strong>RUN</Strong> / Stop")));
runStopButton.addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
setSimRunning(!simIsRunning());
}
});
/*
dumpMatrixButton = new Button("Dump Matrix");
dumpMatrixButton.addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) { dumpMatrix = true; }});
verticalPanel.add(dumpMatrixButton);// IES for debugging
*/
if (LoadFile.isSupported())
verticalPanel.add(loadFileInput = new LoadFile(this));
Label l;
verticalPanel.add(l = new Label(LS("Simulation Speed")));
l.addStyleName("topSpace");
// was max of 140
verticalPanel.add( speedBar = new Scrollbar(Scrollbar.HORIZONTAL, 3, 1, 0, 260));
verticalPanel.add( l = new Label(LS("Current Speed")));
l.addStyleName("topSpace");
currentBar = new Scrollbar(Scrollbar.HORIZONTAL, 50, 1, 1, 100);
verticalPanel.add(currentBar);
verticalPanel.add(powerLabel = new Label (LS("Power Brightness")));
powerLabel.addStyleName("topSpace");
verticalPanel.add(powerBar = new Scrollbar(Scrollbar.HORIZONTAL,
50, 1, 1, 100));
setPowerBarEnable();
// verticalPanel.add(new Label(""));
// Font f = new Font("SansSerif", 0, 10);
l = new Label(LS("Current Circuit:"));
l.addStyleName("topSpace");
// l.setFont(f);
titleLabel = new Label("Label");
// titleLabel.setFont(f);
verticalPanel.add(l);
verticalPanel.add(titleLabel);
verticalPanel.add(iFrame = new Frame("iframe.html"));
iFrame.setWidth(VERTICALPANELWIDTH+"px");
iFrame.setHeight("100 px");
iFrame.getElement().setAttribute("scrolling", "no");
setGrid();
elmList = new Vector<CircuitElm>();
// setupList = new Vector();
undoStack = new Vector<String>();
redoStack = new Vector<String>();
scopes = new Scope[20];
scopeColCount = new int[20];
scopeCount = 0;
random = new Random();
// cv.setBackground(Color.black);
// cv.setForeground(Color.lightGray);
elmMenuBar = new MenuBar(true);
elmMenuBar.addItem(elmEditMenuItem = new MenuItem(LS("Edit"),new MyCommand("elm","edit")));
elmMenuBar.addItem(elmScopeMenuItem = new MenuItem(LS("View in Scope"), new MyCommand("elm","viewInScope")));
elmMenuBar.addItem(elmCutMenuItem = new MenuItem(LS("Cut"),new MyCommand("elm","cut")));
elmMenuBar.addItem(elmCopyMenuItem = new MenuItem(LS("Copy"),new MyCommand("elm","copy")));
elmMenuBar.addItem(elmDeleteMenuItem = new MenuItem(LS("Delete"),new MyCommand("elm","delete")));
elmMenuBar.addItem( new MenuItem(LS("Duplicate"),new MyCommand("elm","duplicate")));
elmMenuBar.addItem(elmFlipMenuItem = new MenuItem(LS("Swap Terminals"),new MyCommand("elm","flip")));
scopeMenuBar = buildScopeMenu(false);
transScopeMenuBar = buildScopeMenu(true);
if (startCircuitText != null) {
getSetupList(false);
readSetup(startCircuitText, true);
} else {
if (stopMessage == null && startCircuitLink!=null) {
readSetup(null, 0, false, true);
getSetupList(false);
ImportFromDropboxDialog.setSim(this);
ImportFromDropboxDialog.doImportDropboxLink(startCircuitLink, false);
} else {
readSetup(null, 0, false, true);
if (stopMessage == null && startCircuit != null) {
getSetupList(false);
readSetupFile(startCircuit, startLabel, true);
}
else
getSetupList(true);
}
}
enableUndoRedo();
enablePaste();
setiFrameHeight();
cv.addMouseDownHandler(this);
cv.addMouseMoveHandler(this);
cv.addMouseOutHandler(this);
cv.addMouseUpHandler(this);
cv.addClickHandler(this);
cv.addDoubleClickHandler(this);
doTouchHandlers(cv.getCanvasElement());
cv.addDomHandler(this, ContextMenuEvent.getType());
menuBar.addDomHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
doMainMenuChecks();
}
}, ClickEvent.getType());
Event.addNativePreviewHandler(this);
cv.addMouseWheelHandler(this);
setSimRunning(true);
// setup timer
timer.scheduleRepeating(FASTTIMER);
}
MenuItem menuItemWithShortcut(String text, String shortcut, MyCommand cmd) {
final String edithtml="<div style=\"display:inline-block;width:80px;\">";
String sn=edithtml + text + "</div>" + shortcut;
return new MenuItem(SafeHtmlUtils.fromTrustedString(sn), cmd);
}
boolean getOptionFromStorage(String key, boolean val) {
Storage stor = Storage.getLocalStorageIfSupported();
if (stor == null)
return val;
String s = stor.getItem(key);
if (s == null)
return val;
return s == "true";
}
void setOptionInStorage(String key, boolean val) {
Storage stor = Storage.getLocalStorageIfSupported();
if (stor == null)
return;
stor.setItem(key, val ? "true" : "false");
}
// install touch handlers
// don't feel like rewriting this in java. Anyway, java doesn't let us create mouse
// events and dispatch them.
native void doTouchHandlers(CanvasElement cv) /*-{
// Set up touch events for mobile, etc
var lastTap;
var tmout;
var sim = this;
cv.addEventListener("touchstart", function (e) {
mousePos = getTouchPos(cv, e);
var touch = e.touches[0];
var etype = "mousedown";
clearTimeout(tmout);
if (e.timeStamp-lastTap < 300) {
etype = "dblclick";
} else {
tmout = setTimeout(function() {
sim.@com.lushprojects.circuitjs1.client.CirSim::longPress()();
}, 500);
}
lastTap = e.timeStamp;
var mouseEvent = new MouseEvent(etype, {
clientX: touch.clientX,
clientY: touch.clientY
});
e.preventDefault();
cv.dispatchEvent(mouseEvent);
}, false);
cv.addEventListener("touchend", function (e) {
var mouseEvent = new MouseEvent("mouseup", {});
e.preventDefault();
clearTimeout(tmout);
cv.dispatchEvent(mouseEvent);
}, false);
cv.addEventListener("touchmove", function (e) {
var touch = e.touches[0];
var mouseEvent = new MouseEvent("mousemove", {
clientX: touch.clientX,
clientY: touch.clientY
});
e.preventDefault();
clearTimeout(tmout);
cv.dispatchEvent(mouseEvent);
}, false);
// Get the position of a touch relative to the canvas
function getTouchPos(canvasDom, touchEvent) {
var rect = canvasDom.getBoundingClientRect();
return {
x: touchEvent.touches[0].clientX - rect.left,
y: touchEvent.touches[0].clientY - rect.top
};
}
}-*/;
boolean shown = false;
public void composeMainMenu(MenuBar mainMenuBar) {
mainMenuBar.addItem(getClassCheckItem(LS("Add Wire"), "WireElm"));
mainMenuBar.addItem(getClassCheckItem(LS("Add Resistor"), "ResistorElm"));
MenuBar passMenuBar = new MenuBar(true);
passMenuBar.addItem(getClassCheckItem(LS("Add Capacitor"), "CapacitorElm"));
passMenuBar.addItem(getClassCheckItem(LS("Add Capacitor (polarized)"), "PolarCapacitorElm"));
passMenuBar.addItem(getClassCheckItem(LS("Add Inductor"), "InductorElm"));
passMenuBar.addItem(getClassCheckItem(LS("Add Switch"), "SwitchElm"));
passMenuBar.addItem(getClassCheckItem(LS("Add Push Switch"), "PushSwitchElm"));
passMenuBar.addItem(getClassCheckItem(LS("Add SPDT Switch"), "Switch2Elm"));
passMenuBar.addItem(getClassCheckItem(LS("Add Potentiometer"), "PotElm"));
passMenuBar.addItem(getClassCheckItem(LS("Add Transformer"), "TransformerElm"));
passMenuBar.addItem(getClassCheckItem(LS("Add Tapped Transformer"), "TappedTransformerElm"));
passMenuBar.addItem(getClassCheckItem(LS("Add Transmission Line"), "TransLineElm"));
passMenuBar.addItem(getClassCheckItem(LS("Add Relay"), "RelayElm"));
passMenuBar.addItem(getClassCheckItem(LS("Add Memristor"), "MemristorElm"));
passMenuBar.addItem(getClassCheckItem(LS("Add Spark Gap"), "SparkGapElm"));
mainMenuBar.addItem(SafeHtmlUtils.fromTrustedString(CheckboxMenuItem.checkBoxHtml+LS(" </div>Passive Components")), passMenuBar);
MenuBar inputMenuBar = new MenuBar(true);
inputMenuBar.addItem(getClassCheckItem(LS("Add Ground"), "GroundElm"));
inputMenuBar.addItem(getClassCheckItem(LS("Add Voltage Source (2-terminal)"), "DCVoltageElm"));
inputMenuBar.addItem(getClassCheckItem(LS("Add A/C Voltage Source (2-terminal)"), "ACVoltageElm"));
inputMenuBar.addItem(getClassCheckItem(LS("Add Voltage Source (1-terminal)"), "RailElm"));
inputMenuBar.addItem(getClassCheckItem(LS("Add A/C Voltage Source (1-terminal)"), "ACRailElm"));
inputMenuBar.addItem(getClassCheckItem(LS("Add Square Wave Source (1-terminal)"), "SquareRailElm"));
inputMenuBar.addItem(getClassCheckItem(LS("Add Clock"), "ClockElm"));
inputMenuBar.addItem(getClassCheckItem(LS("Add A/C Sweep"), "SweepElm"));
inputMenuBar.addItem(getClassCheckItem(LS("Add Variable Voltage"), "VarRailElm"));
inputMenuBar.addItem(getClassCheckItem(LS("Add Antenna"), "AntennaElm"));
inputMenuBar.addItem(getClassCheckItem(LS("Add AM Source"), "AMElm"));
inputMenuBar.addItem(getClassCheckItem(LS("Add FM Source"), "FMElm"));
inputMenuBar.addItem(getClassCheckItem(LS("Add Current Source"), "CurrentElm"));
inputMenuBar.addItem(getClassCheckItem(LS("Add Noise Generator"), "NoiseElm"));
mainMenuBar.addItem(SafeHtmlUtils.fromTrustedString(CheckboxMenuItem.checkBoxHtml+LS(" </div>Inputs and Sources")), inputMenuBar);
MenuBar outputMenuBar = new MenuBar(true);
outputMenuBar.addItem(getClassCheckItem(LS("Add Analog Output"), "OutputElm"));
outputMenuBar.addItem(getClassCheckItem(LS("Add LED"), "LEDElm"));
outputMenuBar.addItem(getClassCheckItem(LS("Add Lamp"), "LampElm"));
outputMenuBar.addItem(getClassCheckItem(LS("Add Text"), "TextElm"));
outputMenuBar.addItem(getClassCheckItem(LS("Add Box"), "BoxElm"));
outputMenuBar.addItem(getClassCheckItem(LS("Add Voltmeter/Scobe Probe"), "ProbeElm"));
outputMenuBar.addItem(getClassCheckItem(LS("Add Labeled Node"), "LabeledNodeElm"));
outputMenuBar.addItem(getClassCheckItem(LS("Add Test Point"), "TestPointElm"));
outputMenuBar.addItem(getClassCheckItem(LS("Add Ammeter"), "AmmeterElm"));
outputMenuBar.addItem(getClassCheckItem(LS("Add Data Export"), "DataRecorderElm"));
outputMenuBar.addItem(getClassCheckItem(LS("Add Audio Output"), "AudioOutputElm"));
mainMenuBar.addItem(SafeHtmlUtils.fromTrustedString(CheckboxMenuItem.checkBoxHtml+LS(" </div>Outputs and Labels")), outputMenuBar);
MenuBar activeMenuBar = new MenuBar(true);
activeMenuBar.addItem(getClassCheckItem(LS("Add Diode"), "DiodeElm"));
activeMenuBar.addItem(getClassCheckItem(LS("Add Zener Diode"), "ZenerElm"));
activeMenuBar.addItem(getClassCheckItem(LS("Add Transistor (bipolar, NPN)"), "NTransistorElm"));
activeMenuBar.addItem(getClassCheckItem(LS("Add Transistor (bipolar, PNP)"), "PTransistorElm"));
activeMenuBar.addItem(getClassCheckItem(LS("Add MOSFET (N-Channel)"), "NMosfetElm"));
activeMenuBar.addItem(getClassCheckItem(LS("Add MOSFET (P-Channel)"), "PMosfetElm"));
activeMenuBar.addItem(getClassCheckItem(LS("Add JFET (N-Channel)"), "NJfetElm"));
activeMenuBar.addItem(getClassCheckItem(LS("Add JFET (P-Channel)"), "PJfetElm"));
activeMenuBar.addItem(getClassCheckItem(LS("Add SCR"), "SCRElm"));
activeMenuBar.addItem(getClassCheckItem(LS("Add Darlington Pair (NPN)"), "NDarlingtonElm"));
activeMenuBar.addItem(getClassCheckItem(LS("Add Darlington Pair (PNP)"), "PDarlingtonElm"));
// activeMenuBar.addItem(getClassCheckItem("Add Varactor/Varicap", "VaractorElm"));
activeMenuBar.addItem(getClassCheckItem(LS("Add Tunnel Diode"), "TunnelDiodeElm"));
activeMenuBar.addItem(getClassCheckItem(LS("Add Triode"), "TriodeElm"));
// activeMenuBar.addItem(getClassCheckItem("Add Diac", "DiacElm"));
// activeMenuBar.addItem(getClassCheckItem("Add Triac", "TriacElm"));
// activeMenuBar.addItem(getClassCheckItem("Add Photoresistor", "PhotoResistorElm"));
// activeMenuBar.addItem(getClassCheckItem("Add Thermistor", "ThermistorElm"));
mainMenuBar.addItem(SafeHtmlUtils.fromTrustedString(CheckboxMenuItem.checkBoxHtml+LS(" </div>Active Components")), activeMenuBar);
MenuBar activeBlocMenuBar = new MenuBar(true);
activeBlocMenuBar.addItem(getClassCheckItem(LS("Add Op Amp (- on top)"), "OpAmpElm"));
activeBlocMenuBar.addItem(getClassCheckItem(LS("Add Op Amp (+ on top)"), "OpAmpSwapElm"));
activeBlocMenuBar.addItem(getClassCheckItem(LS("Add Analog Switch (SPST)"), "AnalogSwitchElm"));
activeBlocMenuBar.addItem(getClassCheckItem(LS("Add Analog Switch (SPDT)"), "AnalogSwitch2Elm"));
activeBlocMenuBar.addItem(getClassCheckItem(LS("Add Tristate Buffer"), "TriStateElm"));
activeBlocMenuBar.addItem(getClassCheckItem(LS("Add Schmitt Trigger"), "SchmittElm"));
activeBlocMenuBar.addItem(getClassCheckItem(LS("Add Schmitt Trigger (Inverting)"), "InvertingSchmittElm"));
activeBlocMenuBar.addItem(getClassCheckItem(LS("Add CCII+"), "CC2Elm"));
activeBlocMenuBar.addItem(getClassCheckItem(LS("Add CCII-"), "CC2NegElm"));
activeBlocMenuBar.addItem(getClassCheckItem(LS("Add Comparator (Hi-Z/GND output)"), "ComparatorElm"));
activeBlocMenuBar.addItem(getClassCheckItem(LS("Add OTA (LM13700 style)"), "OTAElm"));
activeBlocMenuBar.addItem(getClassCheckItem(LS("Add Voltage-Controlled Voltage Source"), "VCVSElm"));
activeBlocMenuBar.addItem(getClassCheckItem(LS("Add Voltage-Controlled Current Source"), "VCCSElm"));
activeBlocMenuBar.addItem(getClassCheckItem(LS("Add Current-Controlled Voltage Source"), "CCVSElm"));
activeBlocMenuBar.addItem(getClassCheckItem(LS("Add Current-Controlled Current Source"), "CCCSElm"));
mainMenuBar.addItem(SafeHtmlUtils.fromTrustedString(CheckboxMenuItem.checkBoxHtml+LS(" </div>Active Building Blocks")), activeBlocMenuBar);
MenuBar gateMenuBar = new MenuBar(true);
gateMenuBar.addItem(getClassCheckItem(LS("Add Logic Input"), "LogicInputElm"));
gateMenuBar.addItem(getClassCheckItem(LS("Add Logic Output"), "LogicOutputElm"));
gateMenuBar.addItem(getClassCheckItem(LS("Add Inverter"), "InverterElm"));
gateMenuBar.addItem(getClassCheckItem(LS("Add NAND Gate"), "NandGateElm"));
gateMenuBar.addItem(getClassCheckItem(LS("Add NOR Gate"), "NorGateElm"));
gateMenuBar.addItem(getClassCheckItem(LS("Add AND Gate"), "AndGateElm"));
gateMenuBar.addItem(getClassCheckItem(LS("Add OR Gate"), "OrGateElm"));
gateMenuBar.addItem(getClassCheckItem(LS("Add XOR Gate"), "XorGateElm"));
mainMenuBar.addItem(SafeHtmlUtils.fromTrustedString(CheckboxMenuItem.checkBoxHtml+LS(" </div>Logic Gates, Input and Output")), gateMenuBar);
MenuBar chipMenuBar = new MenuBar(true);
chipMenuBar.addItem(getClassCheckItem(LS("Add D Flip-Flop"), "DFlipFlopElm"));
chipMenuBar.addItem(getClassCheckItem(LS("Add JK Flip-Flop"), "JKFlipFlopElm"));
chipMenuBar.addItem(getClassCheckItem(LS("Add T Flip-Flop"), "TFlipFlopElm"));
chipMenuBar.addItem(getClassCheckItem(LS("Add 7 Segment LED"), "SevenSegElm"));
chipMenuBar.addItem(getClassCheckItem(LS("Add 7 Segment Decoder"), "SevenSegDecoderElm"));
chipMenuBar.addItem(getClassCheckItem(LS("Add Multiplexer"), "MultiplexerElm"));
chipMenuBar.addItem(getClassCheckItem(LS("Add Demultiplexer"), "DeMultiplexerElm"));
chipMenuBar.addItem(getClassCheckItem(LS("Add SIPO shift register"), "SipoShiftElm"));
chipMenuBar.addItem(getClassCheckItem(LS("Add PISO shift register"), "PisoShiftElm"));
chipMenuBar.addItem(getClassCheckItem(LS("Add Counter"), "CounterElm"));
chipMenuBar.addItem(getClassCheckItem(LS("Add Decade Counter"), "DecadeElm"));
chipMenuBar.addItem(getClassCheckItem(LS("Add Latch"), "LatchElm"));
//chipMenuBar.addItem(getClassCheckItem("Add Static RAM", "SRAMElm"));
chipMenuBar.addItem(getClassCheckItem(LS("Add Sequence generator"), "SeqGenElm"));
chipMenuBar.addItem(getClassCheckItem(LS("Add Full Adder"), "FullAdderElm"));
chipMenuBar.addItem(getClassCheckItem(LS("Add Half Adder"), "HalfAdderElm"));
chipMenuBar.addItem(getClassCheckItem(LS("Add Custom Logic"), "UserDefinedLogicElm"));
mainMenuBar.addItem(SafeHtmlUtils.fromTrustedString(CheckboxMenuItem.checkBoxHtml+LS(" </div>Digital Chips")), chipMenuBar);
MenuBar achipMenuBar = new MenuBar(true);
achipMenuBar.addItem(getClassCheckItem(LS("Add 555 Timer"), "TimerElm"));
achipMenuBar.addItem(getClassCheckItem(LS("Add Phase Comparator"), "PhaseCompElm"));
achipMenuBar.addItem(getClassCheckItem(LS("Add DAC"), "DACElm"));
achipMenuBar.addItem(getClassCheckItem(LS("Add ADC"), "ADCElm"));
achipMenuBar.addItem(getClassCheckItem(LS("Add VCO"), "VCOElm"));
achipMenuBar.addItem(getClassCheckItem(LS("Add Monostable"), "MonostableElm"));
mainMenuBar.addItem(SafeHtmlUtils.fromTrustedString(CheckboxMenuItem.checkBoxHtml+LS(" </div>Analog and Hybrid Chips")), achipMenuBar);
MenuBar otherMenuBar = new MenuBar(true);
CheckboxMenuItem mi;
otherMenuBar.addItem(mi=getClassCheckItem(LS("Drag All"), "DragAll"));
mi.addShortcut(LS("(Alt-drag)"));
otherMenuBar.addItem(mi=getClassCheckItem(LS("Drag Row"), "DragRow"));
mi.addShortcut(LS("(A-S-drag)"));
otherMenuBar.addItem(mi=getClassCheckItem(LS("Drag Column"), "DragColumn"));
mi.addShortcut(isMac ? LS("(A-Cmd-drag)") : LS("(A-M-drag)"));
otherMenuBar.addItem(getClassCheckItem(LS("Drag Selected"), "DragSelected"));
otherMenuBar.addItem(mi=getClassCheckItem(LS("Drag Post"), "DragPost"));
mi.addShortcut("(" + ctrlMetaKey + "-drag)");
mainMenuBar.addItem(SafeHtmlUtils.fromTrustedString(CheckboxMenuItem.checkBoxHtml+LS(" </div>Drag")), otherMenuBar);
mainMenuBar.addItem(mi=getClassCheckItem(LS("Select/Drag Sel"), "Select"));
mi.addShortcut(LS("(space or Shift-drag)"));
}
public void setiFrameHeight() {
if (iFrame==null)
return;
int i;
int cumheight=0;
for (i=0; i < verticalPanel.getWidgetIndex(iFrame); i++) {
if (verticalPanel.getWidget(i) !=loadFileInput) {
cumheight=cumheight+verticalPanel.getWidget(i).getOffsetHeight();
if (verticalPanel.getWidget(i).getStyleName().contains("topSpace"))
cumheight+=12;
}
}
int ih=RootLayoutPanel.get().getOffsetHeight()-MENUBARHEIGHT-cumheight;
if (ih<0)
ih=0;
iFrame.setHeight(ih+"px");
}
MenuBar buildScopeMenu(boolean t) {
MenuBar m = new MenuBar(true);
m.addItem(new CheckboxAlignedMenuItem(LS("Remove Scope"),new MyCommand("scopepop", "remove")));
m.addItem(new CheckboxAlignedMenuItem(LS("Speed 2x"), new MyCommand("scopepop", "speed2")));
m.addItem(new CheckboxAlignedMenuItem(LS("Speed 1/2x"), new MyCommand("scopepop", "speed1/2")));
// m.addItem(new CheckboxAlignedMenuItem(LS("Scale 2x"), new MyCommand("scopepop", "scale")));
CheckboxMenuItem mi;
m.addItem(mi = new CheckboxMenuItem(LS("Max Scale"), new MyCommand("scopepop", "maxscale")));
if (t)
scopeMaxScaleTransMenuItem = mi;
else
scopeMaxScaleMenuItem = mi;
m.addItem(new CheckboxAlignedMenuItem(LS("Stack"), new MyCommand("scopepop", "stack")));
m.addItem(new CheckboxAlignedMenuItem(LS("Unstack"), new MyCommand("scopepop", "unstack")));
m.addItem(new CheckboxAlignedMenuItem(LS("Combine"), new MyCommand("scopepop", "combine")));
if (!t)
m.addItem(scopeRemovePlotMenuItem = new CheckboxAlignedMenuItem(LS("Remove Plot"),new MyCommand("scopepop", "removeplot")));
m.addItem(new CheckboxAlignedMenuItem(LS("Reset"), new MyCommand("scopepop", "reset")));
if (t) {
m.addItem(scopeIbMenuItem = new CheckboxMenuItem(LS("Show Ib"), new MyCommand("scopepop", "showib")));
m.addItem(scopeIcMenuItem = new CheckboxMenuItem(LS("Show Ic"), new MyCommand("scopepop", "showic")));
m.addItem(scopeIeMenuItem = new CheckboxMenuItem(LS("Show Ie"), new MyCommand("scopepop", "showie")));
m.addItem(scopeVbeMenuItem = new CheckboxMenuItem(LS("Show Vbe"), new MyCommand("scopepop", "showvbe")));
m.addItem(scopeVbcMenuItem = new CheckboxMenuItem(LS("Show Vbc"), new MyCommand("scopepop", "showvbc")));
m.addItem(scopeVceMenuItem = new CheckboxMenuItem(LS("Show Vce"), new MyCommand("scopepop", "showvce")));
m.addItem(scopeVceIcMenuItem = new CheckboxMenuItem(LS("Show Vce vs Ic"), new MyCommand("scopepop", "showvcevsic")));
} else {
m.addItem(scopeVMenuItem = new CheckboxMenuItem(LS("Show Voltage"), new MyCommand("scopepop", "showvoltage")));
m.addItem(scopeIMenuItem = new CheckboxMenuItem(LS("Show Current"), new MyCommand("scopepop", "showcurrent")));
m.addItem(scopePowerMenuItem = new CheckboxMenuItem(LS("Show Power Consumed"), new MyCommand("scopepop", "showpower")));
m.addItem(scopeScaleMenuItem = new CheckboxMenuItem(LS("Show Scale"), new MyCommand("scopepop", "showscale")));
m.addItem(scopeMaxMenuItem = new CheckboxMenuItem(LS("Show Peak Value"), new MyCommand("scopepop", "showpeak")));
m.addItem(scopeMinMenuItem = new CheckboxMenuItem(LS("Show Negative Peak Value"), new MyCommand("scopepop", "shownegpeak")));
m.addItem(scopeFreqMenuItem = new CheckboxMenuItem(LS("Show Frequency"), new MyCommand("scopepop", "showfreq")));
m.addItem(scopeFFTMenuItem = new CheckboxMenuItem(LS("Show Spectrum"), new MyCommand("scopepop", "showfft")));
m.addItem(scopeRMSMenuItem = new CheckboxMenuItem(LS("Show RMS Average"), new MyCommand("scopepop", "showrms")));
m.addItem(scopeVIMenuItem = new CheckboxMenuItem(LS("Show V vs I"), new MyCommand("scopepop", "showvvsi")));
m.addItem(scopeXYMenuItem = new CheckboxMenuItem(LS("Plot X/Y"), new MyCommand("scopepop", "plotxy")));
m.addItem(scopeSelectYMenuItem = new CheckboxAlignedMenuItem(LS("Select Y"), new MyCommand("scopepop", "selecty")));
m.addItem(scopeResistMenuItem = new CheckboxMenuItem(LS("Show Resistance"), new MyCommand("scopepop", "showresistance")));
}
return m;
}
CheckboxMenuItem getClassCheckItem(String s, String t) {
// try {
// Class c = Class.forName(t);
String shortcut="";
CircuitElm elm = constructElement(t, 0, 0);
CheckboxMenuItem mi;
// register(c, elm);
if ( elm!=null ) {
if (elm.needsShortcut() ) {
shortcut += (char)elm.getShortcut();
shortcuts[elm.getShortcut()]=t;
}
elm.delete();
}
// else
// GWT.log("Coudn't create class: "+t);
// } catch (Exception ee) {
// ee.printStackTrace();
// }
if (shortcut=="")
mi= new CheckboxMenuItem(s);
else
mi = new CheckboxMenuItem(s, shortcut);
mi.setScheduledCommand(new MyCommand("main", t) );
mainMenuItems.add(mi);
mainMenuItemNames.add(t);
return mi;
}
void centreCircuit() {
Rectangle bounds = getCircuitBounds();
// add some space on edges because bounds calculation is not perfect
double scale = 1;
if (bounds != null)
scale = Math.min(circuitArea.width /(double)(bounds.width+140),
circuitArea.height/(double)(bounds.height+100));
scale = Math.min(scale, 1.5); // Limit scale so we don't create enormous circuits in big windows
// calculate transform so circuit fills most of screen
transform[0] = transform[3] = scale;
transform[1] = transform[2] = transform[4] = transform[5] = 0;
if (bounds != null) {
transform[4] = (circuitArea.width -bounds.width *scale)/2 - bounds.x*scale;
transform[5] = (circuitArea.height-bounds.height*scale)/2 - bounds.y*scale;
}
}
// get circuit bounds. remember this doesn't use setBbox(). That is calculated when we draw
// the circuit, but this needs to be ready before we first draw it, so we use this crude method
Rectangle getCircuitBounds() {
int i;
int minx = 1000, maxx = 0, miny = 1000, maxy = 0;
for (i = 0; i != elmList.size(); i++) {
CircuitElm ce = getElm(i);
// centered text causes problems when trying to center the circuit,
// so we special-case it here
if (!ce.isCenteredText()) {
minx = min(ce.x, min(ce.x2, minx));
maxx = max(ce.x, max(ce.x2, maxx));
}
miny = min(ce.y, min(ce.y2, miny));
maxy = max(ce.y, max(ce.y2, maxy));
}
if (minx > maxx)
return null;
return new Rectangle(minx, miny, maxx-minx, maxy-miny);
}
static final int resct = 6;
long lastTime = 0, lastFrameTime, lastIterTime, secTime = 0;
int frames = 0;
int steps = 0;
int framerate = 0, steprate = 0;
static CirSim theSim;
public void setSimRunning(boolean s) {
if (s) {
simRunning = true;
runStopButton.setHTML(LSHTML("<strong>RUN</strong> / Stop"));
runStopButton.setStylePrimaryName("topButton");
} else {
simRunning = false;
runStopButton.setHTML(LSHTML("Run / <strong>STOP</strong>"));
runStopButton.setStylePrimaryName("topButton-red");
}
}
public boolean simIsRunning() {
return simRunning;
}
// *****************************************************************
// UPDATE CIRCUIT
public void updateCircuit() {
long mystarttime;
long myrunstarttime;
long mydrawstarttime;
// if (winSize == null || winSize.width == 0)
// return;
mystarttime=System.currentTimeMillis();
boolean didAnalyze = analyzeFlag;
if (analyzeFlag) {
analyzeCircuit();
analyzeFlag = false;
}
// if (editDialog != null && editDialog.elm instanceof CircuitElm)
// mouseElm = (CircuitElm) (editDialog.elm);
if (stopElm != null && stopElm != mouseElm)
stopElm.setMouseElm(true);
setupScopes();
Graphics g=new Graphics(backcontext);
CircuitElm.selectColor = Color.cyan;
if (printableCheckItem.getState()) {
CircuitElm.whiteColor = Color.black;
CircuitElm.lightGrayColor = Color.black;
g.setColor(Color.white);
} else {
CircuitElm.whiteColor = Color.white;
CircuitElm.lightGrayColor = Color.lightGray;
g.setColor(Color.black);
}
g.fillRect(0, 0, g.context.getCanvas().getWidth(), g.context.getCanvas().getHeight());
myrunstarttime=System.currentTimeMillis();
if (simRunning) {
try {
runCircuit(didAnalyze);
} catch (Exception e) {
console("exception in runCircuit " + e);
e.printStackTrace();
return;
}
myruntime+=System.currentTimeMillis()-myrunstarttime;
}
long sysTime = System.currentTimeMillis();
if (simRunning) {
if (lastTime != 0) {
int inc = (int) (sysTime - lastTime);
double c = currentBar.getValue();
c = java.lang.Math.exp(c / 3.5 - 14.2);
CircuitElm.currentMult = 1.7 * inc * c;
if (!conventionCheckItem.getState())
CircuitElm.currentMult = -CircuitElm.currentMult;
}
lastTime = sysTime;
} else
lastTime = 0;
if (sysTime - secTime >= 1000) {
framerate = frames;
steprate = steps;
frames = 0;
steps = 0;
secTime = sysTime;
}
CircuitElm.powerMult = Math.exp(powerBar.getValue()/4.762-7);
int i;
// Font oldfont = g.getFont();
Font oldfont = CircuitElm.unitsFont;
g.setFont(oldfont);
// this causes bad behavior on Chrome 55
// g.clipRect(0, 0, circuitArea.width, circuitArea.height);
mydrawstarttime=System.currentTimeMillis();
g.context.setLineCap(LineCap.ROUND);
// draw elements
backcontext.setTransform(transform[0], transform[1], transform[2],
transform[3], transform[4], transform[5]);
for (i = 0; i != elmList.size(); i++) {
if (powerCheckItem.getState())
g.setColor(Color.gray);
/*else if (conductanceCheckItem.getState())
g.setColor(Color.white);*/
getElm(i).draw(g);
}
mydrawtime+=System.currentTimeMillis()-mydrawstarttime;
// draw posts normally
if (mouseMode != CirSim.MODE_DRAG_ROW && mouseMode != CirSim.MODE_DRAG_COLUMN) {
for (i = 0; i != postDrawList.size(); i++)
CircuitElm.drawPost(g, postDrawList.get(i));
}
// for some mouse modes, what matters is not the posts but the endpoints (which are only
// the same for 2-terminal elements). We draw those now if needed
if (tempMouseMode == MODE_DRAG_ROW || tempMouseMode == MODE_DRAG_COLUMN ||
tempMouseMode == MODE_DRAG_POST || tempMouseMode == MODE_DRAG_SELECTED)
for (i = 0; i != elmList.size(); i++) {
CircuitElm ce = getElm(i);
// ce.drawPost(g, ce.x , ce.y );
// ce.drawPost(g, ce.x2, ce.y2);
if (ce!=mouseElm || tempMouseMode!=MODE_DRAG_POST) {
g.setColor(Color.gray);
g.fillOval(ce.x-3, ce.y-3, 7, 7);
g.fillOval(ce.x2-3, ce.y2-3, 7, 7);
} else {
ce.drawHandles(g, Color.cyan);
}
}
// draw handles for elm we're creating
if (tempMouseMode==MODE_SELECT && mouseElm!=null) {
mouseElm.drawHandles(g, Color.cyan);
}
// draw handles for elm we're dragging
if (dragElm != null &&
(dragElm.x != dragElm.x2 || dragElm.y != dragElm.y2)) {
dragElm.draw(g);
dragElm.drawHandles(g, Color.cyan);
}
// draw bad connections. do this last so they will not be overdrawn.
for (i = 0; i != badConnectionList.size(); i++) {
Point cn = badConnectionList.get(i);
g.setColor(Color.red);
g.fillOval(cn.x-3, cn.y-3, 7, 7);
}
if (selectedArea != null) {
g.setColor(CircuitElm.selectColor);
g.drawRect(selectedArea.x, selectedArea.y, selectedArea.width, selectedArea.height);
}
if (crossHairCheckItem.getState() && mouseCursorX>=0
&& mouseCursorX <= circuitArea.width && mouseCursorY <= circuitArea.height) {
g.setColor(Color.gray);
int x = snapGrid(inverseTransformX(mouseCursorX));
int y = snapGrid(inverseTransformY(mouseCursorY));
g.drawLine(x, inverseTransformY(0), x, inverseTransformY(circuitArea.height));
g.drawLine(inverseTransformX(0), y, inverseTransformX(circuitArea.width), y);
}
backcontext.setTransform(1, 0, 0, 1, 0, 0);
if (printableCheckItem.getState())
g.setColor(Color.white);
else
g.setColor(Color.black);
g.fillRect(0, circuitArea.height, circuitArea.width, cv.getCoordinateSpaceHeight()-circuitArea.height);
// g.restore();
g.setFont(oldfont);
int ct = scopeCount;
if (stopMessage != null)
ct = 0;
for (i = 0; i != ct; i++)
scopes[i].draw(g);
if (mouseWasOverSplitter) {
g.setColor(Color.cyan);
g.setLineWidth(4.0);
g.drawLine(0, circuitArea.height-2, circuitArea.width, circuitArea.height-2);
g.setLineWidth(1.0);
}
g.setColor(CircuitElm.whiteColor);
if (stopMessage != null) {
g.drawString(stopMessage, 10, circuitArea.height-10);
} else {
String info[] = new String[10];
if (mouseElm != null) {
if (mousePost == -1) {
mouseElm.getInfo(info);
info[0] = LS(info[0]);
if (info[1] != null)
info[1] = LS(info[1]);
} else
info[0] = "V = " +
CircuitElm.getUnitText(mouseElm.getPostVoltage(mousePost), "V");
/* //shownodes
for (i = 0; i != mouseElm.getPostCount(); i++)
info[0] += " " + mouseElm.nodes[i];
if (mouseElm.getVoltageSourceCount() > 0)
info[0] += ";" + (mouseElm.getVoltageSource()+nodeList.size());
*/
} else {
info[0] = "t = " + CircuitElm.getUnitText(t, "s");
info[1] = LS("time step = ") + CircuitElm.getUnitText(timeStep, "s");
}
if (hintType != -1) {
for (i = 0; info[i] != null; i++)
;
String s = getHint();
if (s == null)
hintType = -1;
else
info[i] = s;
}
int x = 0;
if (ct != 0)
x = scopes[ct-1].rightEdge() + 20;
x = max(x, cv.getCoordinateSpaceWidth()*2/3);
// x=cv.getCoordinateSpaceWidth()*2/3;
// count lines of data
for (i = 0; info[i] != null; i++)
;
int badnodes = badConnectionList.size();
if (badnodes > 0)
info[i++] = badnodes + ((badnodes == 1) ?
LS(" bad connection") : LS(" bad connections"));
int ybase = circuitArea.height;
for (i = 0; info[i] != null; i++)
g.drawString(info[i], x,
ybase+15*(i+1));
}
if (stopElm != null && stopElm != mouseElm)
stopElm.setMouseElm(false);
frames++;
g.setColor(Color.white);
// g.drawString("Framerate: " + CircuitElm.showFormat.format(framerate), 10, 10);
// g.drawString("Steprate: " + CircuitElm.showFormat.format(steprate), 10, 30);
// g.drawString("Steprate/iter: " + CircuitElm.showFormat.format(steprate/getIterCount()), 10, 50);
// g.drawString("iterc: " + CircuitElm.showFormat.format(getIterCount()), 10, 70);
// g.drawString("Frames: "+ frames,10,90);
// g.drawString("ms per frame (other): "+ CircuitElm.showFormat.format((mytime-myruntime-mydrawtime)/myframes),10,110);
// g.drawString("ms per frame (sim): "+ CircuitElm.showFormat.format((myruntime)/myframes),10,130);
// g.drawString("ms per frame (draw): "+ CircuitElm.showFormat.format((mydrawtime)/myframes),10,150);
cvcontext.drawImage(backcontext.getCanvas(), 0.0, 0.0);
lastFrameTime = lastTime;
mytime=mytime+System.currentTimeMillis()-mystarttime;
myframes++;
}
void setupScopes() {
int i;
// check scopes to make sure the elements still exist, and remove
// unused scopes/columns
int pos = -1;
for (i = 0; i < scopeCount; i++) {
if (scopes[i].needToRemove()) {
int j;
for (j = i; j != scopeCount; j++)
scopes[j] = scopes[j+1];
scopeCount--;
i--;
continue;
}
if (scopes[i].position > pos+1)
scopes[i].position = pos+1;
pos = scopes[i].position;
}
while (scopeCount > 0 && scopes[scopeCount-1].getElm() == null)
scopeCount--;
int h = cv.getCoordinateSpaceHeight() - circuitArea.height;
pos = 0;
for (i = 0; i != scopeCount; i++)
scopeColCount[i] = 0;
for (i = 0; i != scopeCount; i++) {
pos = max(scopes[i].position, pos);
scopeColCount[scopes[i].position]++;
}
int colct = pos+1;
int iw = infoWidth;
if (colct <= 2)
iw = iw*3/2;
int w = (cv.getCoordinateSpaceWidth()-iw) / colct;
int marg = 10;
if (w < marg*2)
w = marg*2;
pos = -1;
int colh = 0;
int row = 0;
int speed = 0;
for (i = 0; i != scopeCount; i++) {
Scope s = scopes[i];
if (s.position > pos) {
pos = s.position;
colh = h / scopeColCount[pos];
row = 0;
speed = s.speed;
}
s.stackCount = scopeColCount[pos];
if (s.speed != speed) {
s.speed = speed;
s.resetGraph();
}
Rectangle r = new Rectangle(pos*w, cv.getCoordinateSpaceHeight()-h+colh*row,
w-marg, colh);
row++;
if (!r.equals(s.rect))
s.setRect(r);
}
}
String getHint() {
CircuitElm c1 = getElm(hintItem1);
CircuitElm c2 = getElm(hintItem2);
if (c1 == null || c2 == null)
return null;
if (hintType == HINT_LC) {
if (!(c1 instanceof InductorElm))
return null;
if (!(c2 instanceof CapacitorElm))
return null;
InductorElm ie = (InductorElm) c1;
CapacitorElm ce = (CapacitorElm) c2;
return LS("res.f = ") + CircuitElm.getUnitText(1/(2*pi*Math.sqrt(ie.inductance*
ce.capacitance)), "Hz");
}
if (hintType == HINT_RC) {
if (!(c1 instanceof ResistorElm))
return null;
if (!(c2 instanceof CapacitorElm))
return null;
ResistorElm re = (ResistorElm) c1;
CapacitorElm ce = (CapacitorElm) c2;
return "RC = " + CircuitElm.getUnitText(re.resistance*ce.capacitance,
"s");
}
if (hintType == HINT_3DB_C) {
if (!(c1 instanceof ResistorElm))
return null;
if (!(c2 instanceof CapacitorElm))
return null;
ResistorElm re = (ResistorElm) c1;
CapacitorElm ce = (CapacitorElm) c2;
return LS("f.3db = ") +
CircuitElm.getUnitText(1/(2*pi*re.resistance*ce.capacitance), "Hz");
}
if (hintType == HINT_3DB_L) {
if (!(c1 instanceof ResistorElm))
return null;
if (!(c2 instanceof InductorElm))
return null;
ResistorElm re = (ResistorElm) c1;
InductorElm ie = (InductorElm) c2;
return LS("f.3db = ") +
CircuitElm.getUnitText(re.resistance/(2*pi*ie.inductance), "Hz");
}
if (hintType == HINT_TWINT) {
if (!(c1 instanceof ResistorElm))
return null;
if (!(c2 instanceof CapacitorElm))
return null;
ResistorElm re = (ResistorElm) c1;
CapacitorElm ce = (CapacitorElm) c2;
return LS("fc = ") +
CircuitElm.getUnitText(1/(2*pi*re.resistance*ce.capacitance), "Hz");
}
return null;
}
// public void toggleSwitch(int n) {
// int i;
// for (i = 0; i != elmList.size(); i++) {
// CircuitElm ce = getElm(i);
// if (ce instanceof SwitchElm) {
// n--;
// if (n == 0) {
// ((SwitchElm) ce).toggle();
// analyzeFlag = true;
// cv.repaint();
// return;
// }
// }
// }
// }
void needAnalyze() {
analyzeFlag = true;
//cv.repaint();
}
Vector<CircuitNode> nodeList;
Vector<Point> postDrawList = new Vector<Point>();
Vector<Point> badConnectionList = new Vector<Point>();
CircuitElm voltageSources[];
public CircuitNode getCircuitNode(int n) {
if (n >= nodeList.size())
return null;
return nodeList.elementAt(n);
}
public CircuitElm getElm(int n) {
if (n >= elmList.size())
return null;
return elmList.elementAt(n);
}
public static native void console(String text)
/*-{
console.log(text);
}-*/;
class NodeMapEntry {
int node;
NodeMapEntry() { node = -1; }
NodeMapEntry(int n) { node = n; }
}
// map points to node numbers
HashMap<Point,NodeMapEntry> nodeMap;
HashMap<Point,Integer> postCountMap;
class WireInfo {
WireElm wire;
Vector<CircuitElm> neighbors;
int post;
WireInfo(WireElm w) {
wire = w;
}
}
// info about each wire and its neighbors, used to calculate wire currents
Vector<WireInfo> wireInfoList;
// find groups of nodes connected by wires and map them to the same node. this speeds things
// up considerably by reducing the size of the matrix
void calculateWireClosure() {
int i;
nodeMap = new HashMap<Point,NodeMapEntry>();
// int mergeCount = 0;
wireInfoList = new Vector<WireInfo>();
for (i = 0; i != elmList.size(); i++) {
CircuitElm ce = getElm(i);
if (!(ce instanceof WireElm))
continue;
WireElm we = (WireElm) ce;
we.hasWireInfo = false;
wireInfoList.add(new WireInfo(we));
NodeMapEntry cn = nodeMap.get(ce.getPost(0));
NodeMapEntry cn2 = nodeMap.get(ce.getPost(1));
if (cn != null && cn2 != null) {
// merge nodes; go through map and change all keys pointing to cn2 to point to cn
for (Map.Entry<Point, NodeMapEntry> entry : nodeMap.entrySet()) {
if (entry.getValue() == cn2)
entry.setValue(cn);
}
// mergeCount++;
continue;
}
if (cn != null) {
nodeMap.put(ce.getPost(1), cn);
continue;
}
if (cn2 != null) {
nodeMap.put(ce.getPost(0), cn2);
continue;
}
// new entry
cn = new NodeMapEntry();
nodeMap.put(ce.getPost(0), cn);
nodeMap.put(ce.getPost(1), cn);
}
// console("got " + (groupCount-mergeCount) + " groups with " + nodeMap.size() + " nodes " + mergeCount);
}
// generate info we need to calculate wire currents. Most other elements calculate currents using
// the voltage on their terminal nodes. But wires have the same voltage at both ends, so we need
// to use the neighbors' currents instead.
boolean calcWireInfo() {
int i;
int moved = 0;
for (i = 0; i != wireInfoList.size(); i++) {
WireInfo wi = wireInfoList.get(i);
WireElm wire = wi.wire;
CircuitNode cn1 = nodeList.get(wire.getNode(0)); // both ends of wire have same node #
int j;
Vector<CircuitElm> neighbors0 = new Vector<CircuitElm>();
Vector<CircuitElm> neighbors1 = new Vector<CircuitElm>();
boolean isReady0 = true, isReady1 = true;
// go through elements sharing a node with this wire (may be connected indirectly
// by other wires, but at least it's faster than going through all elements)
for (j = 0; j != cn1.links.size(); j++) {
CircuitNodeLink cnl = cn1.links.get(j);
CircuitElm ce = cnl.elm;
if (ce == wire)
continue;
Point pt = cnl.elm.getPost(cnl.num);
// is this a wire that doesn't have wire info yet? If so we can't use it.
// That would create a circular dependency
boolean notReady = (ce instanceof WireElm && !((WireElm) ce).hasWireInfo);
// which post does this element connect to, if any?
if (pt.x == wire.x && pt.y == wire.y) {
neighbors0.add(ce);
if (notReady) isReady0 = false;
} else if (pt.x == wire.x2 && pt.y == wire.y2) {
neighbors1.add(ce);
if (notReady) isReady1 = false;
}
}
// does one of the posts have all information necessary to calculate current
if (isReady0) {
wi.neighbors = neighbors0;
wi.post = 0;
wire.hasWireInfo = true;
} else if (isReady1) {
wi.neighbors = neighbors1;
wi.post = 1;
wire.hasWireInfo = true;
} else {
// move to the end of the list and try again later
wireInfoList.add(wireInfoList.remove(i--));
moved++;
// console("moved to end " + moved);
if (moved > wireInfoList.size() * 2) {
stop(LS("wire loop detected"), wire);
return false;
}
}
}
return true;
}
void analyzeCircuit() {
if (elmList.isEmpty()) {
postDrawList = new Vector<Point>();
badConnectionList = new Vector<Point>();
return;
}
stopMessage = null;
stopElm = null;
int i, j;
int vscount = 0;
nodeList = new Vector<CircuitNode>();
postCountMap = new HashMap<Point,Integer>();
boolean gotGround = false;
boolean gotRail = false;
CircuitElm volt = null;
calculateWireClosure();
//System.out.println("ac1");
// look for voltage or ground element
for (i = 0; i != elmList.size(); i++) {
CircuitElm ce = getElm(i);
if (ce instanceof GroundElm) {
gotGround = true;
break;
}
if (ce instanceof RailElm)
gotRail = true;
if (volt == null && ce instanceof VoltageElm)
volt = ce;
}
// if no ground, and no rails, then the voltage elm's first terminal
// is ground
if (!gotGround && volt != null && !gotRail) {
CircuitNode cn = new CircuitNode();
Point pt = volt.getPost(0);
nodeList.addElement(cn);
// update node map
NodeMapEntry cln = nodeMap.get(pt);
if (cln != null)
cln.node = 0;
else
nodeMap.put(pt, new NodeMapEntry(0));
} else {
// otherwise allocate extra node for ground
CircuitNode cn = new CircuitNode();
nodeList.addElement(cn);
}
//System.out.println("ac2");
// allocate nodes and voltage sources
LabeledNodeElm.resetNodeList();
for (i = 0; i != elmList.size(); i++) {
CircuitElm ce = getElm(i);
int inodes = ce.getInternalNodeCount();
int ivs = ce.getVoltageSourceCount();
int posts = ce.getPostCount();
// allocate a node for each post and match posts to nodes
for (j = 0; j != posts; j++) {
Point pt = ce.getPost(j);
Integer g = postCountMap.get(pt);
postCountMap.put(pt, g == null ? 1 : g+1);
NodeMapEntry cln = nodeMap.get(pt);
// is this node not in map yet? or is the node number unallocated?
// (we don't allocate nodes before this because changing the allocation order
// of nodes changes circuit behavior and breaks backward compatibility;
// the code below to connect unconnected nodes may connect a different node to ground)
if (cln == null || cln.node == -1) {
CircuitNode cn = new CircuitNode();
CircuitNodeLink cnl = new CircuitNodeLink();
cnl.num = j;
cnl.elm = ce;
cn.links.addElement(cnl);
ce.setNode(j, nodeList.size());
if (cln != null)
cln.node = nodeList.size();
else
nodeMap.put(pt, new NodeMapEntry(nodeList.size()));
nodeList.addElement(cn);
} else {
int n = cln.node;
CircuitNodeLink cnl = new CircuitNodeLink();
cnl.num = j;
cnl.elm = ce;
getCircuitNode(n).links.addElement(cnl);
ce.setNode(j, n);
// if it's the ground node, make sure the node voltage is 0,
// cause it may not get set later
if (n == 0)
ce.setNodeVoltage(j, 0);
}
}
for (j = 0; j != inodes; j++) {
CircuitNode cn = new CircuitNode();
cn.internal = true;
CircuitNodeLink cnl = new CircuitNodeLink();
cnl.num = j+posts;
cnl.elm = ce;
cn.links.addElement(cnl);
ce.setNode(cnl.num, nodeList.size());
nodeList.addElement(cn);
}
vscount += ivs;
}
makePostDrawList();
if (!calcWireInfo())
return;
nodeMap = null; // done with this
voltageSources = new CircuitElm[vscount];
vscount = 0;
circuitNonLinear = false;
//System.out.println("ac3");
// determine if circuit is nonlinear
for (i = 0; i != elmList.size(); i++) {
CircuitElm ce = getElm(i);
if (ce.nonLinear())
circuitNonLinear = true;
int ivs = ce.getVoltageSourceCount();
for (j = 0; j != ivs; j++) {
voltageSources[vscount] = ce;
ce.setVoltageSource(j, vscount++);
}
}
voltageSourceCount = vscount;
int matrixSize = nodeList.size()-1 + vscount;
circuitMatrix = new double[matrixSize][matrixSize];
circuitRightSide = new double[matrixSize];
origMatrix = new double[matrixSize][matrixSize];
origRightSide = new double[matrixSize];
circuitMatrixSize = circuitMatrixFullSize = matrixSize;
circuitRowInfo = new RowInfo[matrixSize];
circuitPermute = new int[matrixSize];
for (i = 0; i != matrixSize; i++)
circuitRowInfo[i] = new RowInfo();
circuitNeedsMap = false;
// stamp linear circuit elements
for (i = 0; i != elmList.size(); i++) {
CircuitElm ce = getElm(i);
ce.stamp();
}
//System.out.println("ac4");
// determine nodes that are not connected indirectly to ground
boolean closure[] = new boolean[nodeList.size()];
boolean changed = true;
closure[0] = true;
while (changed) {
changed = false;
for (i = 0; i != elmList.size(); i++) {
CircuitElm ce = getElm(i);
if (ce instanceof WireElm)
continue;
// loop through all ce's nodes to see if they are connected
// to other nodes not in closure
for (j = 0; j < ce.getConnectionNodeCount(); j++) {
if (!closure[ce.getConnectionNode(j)]) {
if (ce.hasGroundConnection(j))
closure[ce.getConnectionNode(j)] = changed = true;
continue;
}
int k;
for (k = 0; k != ce.getConnectionNodeCount(); k++) {
if (j == k)
continue;
int kn = ce.getConnectionNode(k);
if (ce.getConnection(j, k) && !closure[kn]) {
closure[kn] = true;
changed = true;
}
}
}
}
if (changed)
continue;
// connect one of the unconnected nodes to ground with a big resistor, then try again
for (i = 0; i != nodeList.size(); i++)
if (!closure[i] && !getCircuitNode(i).internal) {
console("node " + i + " unconnected");
stampResistor(0, i, 1e8);
closure[i] = true;
changed = true;
break;
}
}
//System.out.println("ac5");
for (i = 0; i != elmList.size(); i++) {
CircuitElm ce = getElm(i);
// look for inductors with no current path
if (ce instanceof InductorElm) {
FindPathInfo fpi = new FindPathInfo(FindPathInfo.INDUCT, ce,
ce.getNode(1));
// first try findPath with maximum depth of 5, to avoid slowdowns
if (!fpi.findPath(ce.getNode(0), 5) &&
!fpi.findPath(ce.getNode(0))) {
// console(ce + " no path");
ce.reset();
}
}
// look for current sources with no current path
if (ce instanceof CurrentElm) {
CurrentElm cur = (CurrentElm) ce;
FindPathInfo fpi = new FindPathInfo(FindPathInfo.INDUCT, ce,
ce.getNode(1));
if (!fpi.findPath(ce.getNode(0))) {
cur.stampCurrentSource(true);
} else
cur.stampCurrentSource(false);
}
if (ce instanceof VCCSElm) {
VCCSElm cur = (VCCSElm) ce;
FindPathInfo fpi = new FindPathInfo(FindPathInfo.INDUCT, ce,
cur.getOutputNode(0));
if (cur.hasCurrentOutput() && !fpi.findPath(cur.getOutputNode(1))) {
cur.broken = true;
} else
cur.broken = false;
}
// look for voltage source loops
// IES
if ((ce instanceof VoltageElm && ce.getPostCount() == 2) /*|| ce instanceof WireElm*/) {
FindPathInfo fpi = new FindPathInfo(FindPathInfo.VOLTAGE, ce,
ce.getNode(1));
if (fpi.findPath(ce.getNode(0))) {
stop(LS("Voltage source/wire loop with no resistance!"), ce);
return;
}
}
// look for shorted caps, or caps w/ voltage but no R
if (ce instanceof CapacitorElm) {
FindPathInfo fpi = new FindPathInfo(FindPathInfo.SHORT, ce,
ce.getNode(1));
if (fpi.findPath(ce.getNode(0))) {
console(ce + " shorted");
ce.reset();
} else {
// a capacitor loop used to cause a matrix error. but we changed the capacitor model
// so it works fine now. The only issue is if a capacitor is added in parallel with
// another capacitor with a nonzero voltage; in that case we will get oscillation unless
// we reset both capacitors to have the same voltage. Rather than check for that, we just
// give an error.
fpi = new FindPathInfo(FindPathInfo.CAP_V, ce, ce.getNode(1));
if (fpi.findPath(ce.getNode(0))) {
stop(LS("Capacitor loop with no resistance!"), ce);
return;
}
}
}
}
//System.out.println("ac6");
simplifyMatrix(matrixSize);
/*
System.out.println("matrixSize = " + matrixSize + " " + circuitNonLinear);
for (j = 0; j != circuitMatrixSize; j++) {
for (i = 0; i != circuitMatrixSize; i++)
System.out.print(circuitMatrix[j][i] + " ");
System.out.print(" " + circuitRightSide[j] + "\n");
}
System.out.print("\n");*/
// if a matrix is linear, we can do the lu_factor here instead of
// needing to do it every frame
if (!circuitNonLinear) {
if (!lu_factor(circuitMatrix, circuitMatrixSize, circuitPermute)) {
stop(LS("Singular matrix!"), null);
return;
}
}
// show resistance in voltage sources if there's only one
boolean gotVoltageSource = false;
showResistanceInVoltageSources = true;
for (i = 0; i != elmList.size(); i++) {
CircuitElm ce = getElm(i);
if (ce instanceof VoltageElm) {
if (gotVoltageSource)
showResistanceInVoltageSources = false;
else
gotVoltageSource = true;
}
}
}
// simplify the matrix; this speeds things up quite a bit, especially for
// digital circuits
void simplifyMatrix(int matrixSize) {
int i, j;
for (i = 0; i != matrixSize; i++) {
int qp = -1;
double qv = 0;
RowInfo re = circuitRowInfo[i];
/*System.out.println("row " + i + " " + re.lsChanges + " " + re.rsChanges + " " +
re.dropRow);*/
if (re.lsChanges || re.dropRow || re.rsChanges)
continue;
double rsadd = 0;
// look for rows that can be removed
for (j = 0; j != matrixSize; j++) {
double q = circuitMatrix[i][j];
if (circuitRowInfo[j].type == RowInfo.ROW_CONST) {
// keep a running total of const values that have been
// removed already
rsadd -= circuitRowInfo[j].value*q;
continue;
}
// ignore zeroes
if (q == 0)
continue;
// keep track of first nonzero element that is not ROW_CONST
if (qp == -1) {
qp = j;
qv = q;
continue;
}
// more than one nonzero element? give up
break;
}
if (j == matrixSize) {
if (qp == -1) {
stop(LS("Matrix error"), null);
return;
}
RowInfo elt = circuitRowInfo[qp];
// we found a row with only one nonzero nonconst entry; that value
// is a constant
if (elt.type != RowInfo.ROW_NORMAL) {
System.out.println("type already " + elt.type + " for " + qp + "!");
continue;
}
elt.type = RowInfo.ROW_CONST;
// console("ROW_CONST " + i + " " + rsadd);
elt.value = (circuitRightSide[i]+rsadd)/qv;
circuitRowInfo[i].dropRow = true;
i = -1; // start over from scratch
}
}
//System.out.println("ac7");
// find size of new matrix
int nn = 0;
for (i = 0; i != matrixSize; i++) {
RowInfo elt = circuitRowInfo[i];
if (elt.type == RowInfo.ROW_NORMAL) {
elt.mapCol = nn++;
//System.out.println("col " + i + " maps to " + elt.mapCol);
continue;
}
if (elt.type == RowInfo.ROW_CONST)
elt.mapCol = -1;
}
// make the new, simplified matrix
int newsize = nn;
double newmatx[][] = new double[newsize][newsize];
double newrs [] = new double[newsize];
int ii = 0;
for (i = 0; i != matrixSize; i++) {
RowInfo rri = circuitRowInfo[i];
if (rri.dropRow) {
rri.mapRow = -1;
continue;
}
newrs[ii] = circuitRightSide[i];
rri.mapRow = ii;
//System.out.println("Row " + i + " maps to " + ii);
for (j = 0; j != matrixSize; j++) {
RowInfo ri = circuitRowInfo[j];
if (ri.type == RowInfo.ROW_CONST)
newrs[ii] -= ri.value*circuitMatrix[i][j];
else
newmatx[ii][ri.mapCol] += circuitMatrix[i][j];
}
ii++;
}
// console("old size = " + matrixSize + " new size = " + newsize);
circuitMatrix = newmatx;
circuitRightSide = newrs;
matrixSize = circuitMatrixSize = newsize;
for (i = 0; i != matrixSize; i++)
origRightSide[i] = circuitRightSide[i];
for (i = 0; i != matrixSize; i++)
for (j = 0; j != matrixSize; j++)
origMatrix[i][j] = circuitMatrix[i][j];
circuitNeedsMap = true;
}
// make list of posts we need to draw. posts shared by 2 elements should be hidden, all
// others should be drawn. We can't use the node list anymore because wires have the same
// node number at both ends.
void makePostDrawList() {
postDrawList = new Vector<Point>();
badConnectionList = new Vector<Point>();
for (Map.Entry<Point, Integer> entry : postCountMap.entrySet()) {
if (entry.getValue() != 2)
postDrawList.add(entry.getKey());
// look for bad connections, posts not connected to other elements which intersect
// other elements' bounding boxes
if (entry.getValue() == 1) {
int j;
boolean bad = false;
Point cn = entry.getKey();
for (j = 0; j != elmList.size() && !bad; j++) {
CircuitElm ce = getElm(j);
if ( ce instanceof GraphicElm )
continue;
// does this post intersect elm's bounding box?
if (!ce.boundingBox.contains(cn.x, cn.y))
continue;
int k;
// does this post belong to the elm?
int pc = ce.getPostCount();
for (k = 0; k != pc; k++)
if (ce.getPost(k).equals(cn))
break;
if (k == pc)
bad = true;
}
if (bad)
badConnectionList.add(cn);
}
}
postCountMap = null;
}
class FindPathInfo {
static final int INDUCT = 1;
static final int VOLTAGE = 2;
static final int SHORT = 3;
static final int CAP_V = 4;
boolean used[];
int dest;
CircuitElm firstElm;
int type;
FindPathInfo(int t, CircuitElm e, int d) {
dest = d;
type = t;
firstElm = e;
used = new boolean[nodeList.size()];
}
boolean findPath(int n1) { return findPath(n1, -1); }
boolean findPath(int n1, int depth) {
if (n1 == dest)
return true;
if (depth-- == 0)
return false;
if (used[n1]) {
//System.out.println("used " + n1);
return false;
}
used[n1] = true;
int i;
for (i = 0; i != elmList.size(); i++) {
CircuitElm ce = getElm(i);
if (ce == firstElm)
continue;
if (type == INDUCT) {
// inductors need a path free of current sources
if (ce instanceof CurrentElm)
continue;
}
if (type == VOLTAGE) {
// when checking for voltage loops, we only care about voltage sources/wires
if (!(ce.isWire() || ce instanceof VoltageElm))
continue;
}
// when checking for shorts, just check wires
if (type == SHORT && !ce.isWire())
continue;
if (type == CAP_V) {
// checking for capacitor/voltage source loops
if (!(ce.isWire() || ce instanceof CapacitorElm || ce instanceof VoltageElm))
continue;
}
if (n1 == 0) {
// look for posts which have a ground connection;
// our path can go through ground
int j;
for (j = 0; j != ce.getConnectionNodeCount(); j++)
if (ce.hasGroundConnection(j) &&
findPath(ce.getConnectionNode(j), depth)) {
used[n1] = false;
return true;
}
}
int j;
for (j = 0; j != ce.getConnectionNodeCount(); j++) {
//System.out.println(ce + " " + ce.getNode(j));
if (ce.getConnectionNode(j) == n1)
break;
}
if (j == ce.getConnectionNodeCount())
continue;
if (ce.hasGroundConnection(j) && findPath(0, depth)) {
//System.out.println(ce + " has ground");
used[n1] = false;
return true;
}
if (type == INDUCT && ce instanceof InductorElm) {
// inductors can use paths with other inductors of matching current
double c = ce.getCurrent();
if (j == 0)
c = -c;
//System.out.println("matching " + c + " to " + firstElm.getCurrent());
//System.out.println(ce + " " + firstElm);
if (Math.abs(c-firstElm.getCurrent()) > 1e-10)
continue;
}
int k;
for (k = 0; k != ce.getConnectionNodeCount(); k++) {
if (j == k)
continue;
// console(ce + " " + ce.getNode(j) + "-" + ce.getNode(k));
if (ce.getConnection(j, k) && findPath(ce.getConnectionNode(k), depth)) {
//System.out.println("got findpath " + n1);
used[n1] = false;
return true;
}
//System.out.println("back on findpath " + n1);
}
}
used[n1] = false;
//System.out.println(n1 + " failed");
return false;
}
}
void stop(String s, CircuitElm ce) {
stopMessage = s;
circuitMatrix = null; // causes an exception
stopElm = ce;
setSimRunning(false);
analyzeFlag = false;
// cv.repaint();
}
// control voltage source vs with voltage from n1 to n2 (must
// also call stampVoltageSource())
void stampVCVS(int n1, int n2, double coef, int vs) {
int vn = nodeList.size()+vs;
stampMatrix(vn, n1, coef);
stampMatrix(vn, n2, -coef);
}
// stamp independent voltage source #vs, from n1 to n2, amount v
void stampVoltageSource(int n1, int n2, int vs, double v) {
int vn = nodeList.size()+vs;
stampMatrix(vn, n1, -1);
stampMatrix(vn, n2, 1);
stampRightSide(vn, v);
stampMatrix(n1, vn, 1);
stampMatrix(n2, vn, -1);
}
// use this if the amount of voltage is going to be updated in doStep()
void stampVoltageSource(int n1, int n2, int vs) {
int vn = nodeList.size()+vs;
stampMatrix(vn, n1, -1);
stampMatrix(vn, n2, 1);
stampRightSide(vn);
stampMatrix(n1, vn, 1);
stampMatrix(n2, vn, -1);
}
void updateVoltageSource(int n1, int n2, int vs, double v) {
int vn = nodeList.size()+vs;
stampRightSide(vn, v);
}
void stampResistor(int n1, int n2, double r) {
double r0 = 1/r;
if (Double.isNaN(r0) || Double.isInfinite(r0)) {
System.out.print("bad resistance " + r + " " + r0 + "\n");
int a = 0;
a /= a;
}
stampMatrix(n1, n1, r0);
stampMatrix(n2, n2, r0);
stampMatrix(n1, n2, -r0);
stampMatrix(n2, n1, -r0);
}
void stampConductance(int n1, int n2, double r0) {
stampMatrix(n1, n1, r0);
stampMatrix(n2, n2, r0);
stampMatrix(n1, n2, -r0);
stampMatrix(n2, n1, -r0);
}
// current from cn1 to cn2 is equal to voltage from vn1 to 2, divided by g
void stampVCCurrentSource(int cn1, int cn2, int vn1, int vn2, double g) {
stampMatrix(cn1, vn1, g);
stampMatrix(cn2, vn2, g);
stampMatrix(cn1, vn2, -g);
stampMatrix(cn2, vn1, -g);
}
void stampCurrentSource(int n1, int n2, double i) {
stampRightSide(n1, -i);
stampRightSide(n2, i);
}
// stamp a current source from n1 to n2 depending on current through vs
void stampCCCS(int n1, int n2, int vs, double gain) {
int vn = nodeList.size()+vs;
stampMatrix(n1, vn, gain);
stampMatrix(n2, vn, -gain);
}
// stamp value x in row i, column j, meaning that a voltage change
// of dv in node j will increase the current into node i by x dv.
// (Unless i or j is a voltage source node.)
void stampMatrix(int i, int j, double x) {
if (i > 0 && j > 0) {
if (circuitNeedsMap) {
i = circuitRowInfo[i-1].mapRow;
RowInfo ri = circuitRowInfo[j-1];
if (ri.type == RowInfo.ROW_CONST) {
//System.out.println("Stamping constant " + i + " " + j + " " + x);
circuitRightSide[i] -= x*ri.value;
return;
}
j = ri.mapCol;
//System.out.println("stamping " + i + " " + j + " " + x);
} else {
i--;
j--;
}
circuitMatrix[i][j] += x;
}
}
// stamp value x on the right side of row i, representing an
// independent current source flowing into node i
void stampRightSide(int i, double x) {
if (i > 0) {
if (circuitNeedsMap) {
i = circuitRowInfo[i-1].mapRow;
//System.out.println("stamping " + i + " " + x);
} else
i--;
circuitRightSide[i] += x;
}
}
// indicate that the value on the right side of row i changes in doStep()
void stampRightSide(int i) {
//System.out.println("rschanges true " + (i-1));
if (i > 0)
circuitRowInfo[i-1].rsChanges = true;
}
// indicate that the values on the left side of row i change in doStep()
void stampNonLinear(int i) {
if (i > 0)
circuitRowInfo[i-1].lsChanges = true;
}
double getIterCount() {
// IES - remove interaction
if (speedBar.getValue() == 0)
return 0;
return .1*Math.exp((speedBar.getValue()-61)/24.);
}
// we need to calculate wire currents for every iteration if someone is viewing a wire in the
// scope. Otherwise we can do it only once per frame.
boolean canDelayWireProcessing() {
int i;
for (i = 0; i != scopeCount; i++)
if (scopes[i].getElm() instanceof WireElm)
return false;
return true;
}
boolean converged;
int subIterations;
void runCircuit(boolean didAnalyze) {
if (circuitMatrix == null || elmList.size() == 0) {
circuitMatrix = null;
return;
}
int iter;
//int maxIter = getIterCount();
boolean debugprint = dumpMatrix;
dumpMatrix = false;
long steprate = (long) (160*getIterCount());
long tm = System.currentTimeMillis();
long lit = lastIterTime;
if (lit == 0) {
lastIterTime = tm;
return;
}
// Check if we don't need to run simulation (for very slow simulation speeds).
// If the circuit changed, do at least one iteration to make sure everything is consistent.
if (1000 >= steprate*(tm-lastIterTime) && !didAnalyze)
return;
boolean delayWireProcessing = canDelayWireProcessing();
for (iter = 1; ; iter++) {
int i, j, k, subiter;
for (i = 0; i != elmList.size(); i++) {
CircuitElm ce = getElm(i);
ce.startIteration();
}
steps++;
final int subiterCount = 5000;
for (subiter = 0; subiter != subiterCount; subiter++) {
converged = true;
subIterations = subiter;
for (i = 0; i != circuitMatrixSize; i++)
circuitRightSide[i] = origRightSide[i];
if (circuitNonLinear) {
for (i = 0; i != circuitMatrixSize; i++)
for (j = 0; j != circuitMatrixSize; j++)
circuitMatrix[i][j] = origMatrix[i][j];
}
for (i = 0; i != elmList.size(); i++) {
CircuitElm ce = getElm(i);
ce.doStep();
}
if (stopMessage != null)
return;
boolean printit = debugprint;
debugprint = false;
for (j = 0; j != circuitMatrixSize; j++) {
for (i = 0; i != circuitMatrixSize; i++) {
double x = circuitMatrix[i][j];
if (Double.isNaN(x) || Double.isInfinite(x)) {
stop(LS("nan/infinite matrix!"), null);
return;
}
}
}
if (printit) {
for (j = 0; j != circuitMatrixSize; j++) {
String x = "";
for (i = 0; i != circuitMatrixSize; i++)
x += circuitMatrix[j][i] + ",";
x += "\n";
console(x);
}
console("");
}
if (circuitNonLinear) {
if (converged && subiter > 0)
break;
if (!lu_factor(circuitMatrix, circuitMatrixSize,
circuitPermute)) {
stop(LS("Singular matrix!"), null);
return;
}
}
lu_solve(circuitMatrix, circuitMatrixSize, circuitPermute,
circuitRightSide);
for (j = 0; j != circuitMatrixFullSize; j++) {
RowInfo ri = circuitRowInfo[j];
double res = 0;
if (ri.type == RowInfo.ROW_CONST)
res = ri.value;
else
res = circuitRightSide[ri.mapCol];
/*System.out.println(j + " " + res + " " +
ri.type + " " + ri.mapCol);*/
if (Double.isNaN(res)) {
converged = false;
//debugprint = true;
break;
}
if (j < nodeList.size()-1) {
CircuitNode cn = getCircuitNode(j+1);
for (k = 0; k != cn.links.size(); k++) {
CircuitNodeLink cnl = (CircuitNodeLink)
cn.links.elementAt(k);
cnl.elm.setNodeVoltage(cnl.num, res);
}
} else {
int ji = j-(nodeList.size()-1);
//System.out.println("setting vsrc " + ji + " to " + res);
voltageSources[ji].setCurrent(ji, res);
}
}
if (!circuitNonLinear)
break;
}
if (subiter > 5)
console("converged after " + subiter + " iterations\n");
if (subiter == subiterCount) {
stop(LS("Convergence failed!"), null);
break;
}
t += timeStep;
for (i = 0; i != elmList.size(); i++) {
CircuitElm ce = getElm(i);
ce.stepFinished();
}
if (!delayWireProcessing)
calcWireCurrents();
for (i = 0; i != scopeCount; i++)
scopes[i].timeStep();
tm = System.currentTimeMillis();
lit = tm;
if (iter*1000 >= steprate*(tm-lastIterTime) || (tm-lastFrameTime > 500))
break;
} // for (iter = 1; ; iter++)
lastIterTime = lit;
if (delayWireProcessing)
calcWireCurrents();
// System.out.println((System.currentTimeMillis()-lastFrameTime)/(double) iter);
}
// we removed wires from the matrix to speed things up. in order to display wire currents,
// we need to calculate them now.
void calcWireCurrents() {
int i;
// for debugging
//for (i = 0; i != wireInfoList.size(); i++)
// wireInfoList.get(i).wire.setCurrent(-1, 1.23);
for (i = 0; i != wireInfoList.size(); i++) {
WireInfo wi = wireInfoList.get(i);
double cur = 0;
int j;
Point p = wi.wire.getPost(wi.post);
for (j = 0; j != wi.neighbors.size(); j++) {
CircuitElm ce = wi.neighbors.get(j);
cur += ce.getCurrentIntoPoint(p.x, p.y);
}
if (wi.post == 0)
wi.wire.setCurrent(-1, cur);
else
wi.wire.setCurrent(-1, -cur);
}
}
int min(int a, int b) { return (a < b) ? a : b; }
int max(int a, int b) { return (a > b) ? a : b; }
public void resetAction(){
int i;
for (i = 0; i != elmList.size(); i++)
getElm(i).reset();
for (i = 0; i != scopeCount; i++)
scopes[i].resetGraph();
// TODO: Will need to do IE bug fix here?
analyzeFlag = true;
t=0;
setSimRunning(true);
}
public void menuPerformed(String menu, String item) {
if (item=="about")
aboutBox = new AboutBox(circuitjs1.versionString);
if (item=="importfromlocalfile") {
pushUndo();
loadFileInput.click();
}
if (item=="importfromtext") {
importFromTextDialog = new ImportFromTextDialog(this);
}
if (item=="importfromdropbox") {
importFromDropboxDialog = new ImportFromDropboxDialog(this);
}
if (item=="exportasurl") {
doExportAsUrl();
}
if (item=="exportaslocalfile")
doExportAsLocalFile();
if (item=="exportastext")
doExportAsText();
if (item=="exporttodropbox")
doExportToDropbox();
if ((menu=="elm" || menu=="scopepop") && contextPanel!=null)
contextPanel.hide();
if (menu=="options" && item=="other")
doEdit(new EditOptions(this));
if (item=="undo")
doUndo();
if (item=="redo")
doRedo();
if (item == "cut") {
if (menu!="elm")
menuElm = null;
doCut();
}
if (item == "copy") {
if (menu!="elm")
menuElm = null;
doCopy();
}
if (item=="paste")
doPaste(null);
if (item=="duplicate") {
if (menu!="elm")
menuElm = null;
doDuplicate();
}
if (item=="flip")
doFlip();
if (item=="selectAll")
doSelectAll();
// if (e.getSource() == exitItem) {
// destroyFrame();
// return;
// }
if (item=="centrecircuit") {
pushUndo();
centreCircuit();
}
if (item=="stackAll")
stackAll();
if (item=="unstackAll")
unstackAll();
if (item=="combineAll")
combineAll();
if (item=="zoomin")
zoomCircuit(20);
if (item=="zoomout")
zoomCircuit(-20);
if (item=="zoom100")
setCircuitScale(1);
if (menu=="elm" && item=="edit")
doEdit(menuElm);
if (item=="delete") {
if (menu=="elm")
menuElm = null;
doDelete();
}
if (item=="viewInScope" && menuElm != null) {
int i;
for (i = 0; i != scopeCount; i++)
if (scopes[i].getElm() == null)
break;
if (i == scopeCount) {
if (scopeCount == scopes.length)
return;
scopeCount++;
scopes[i] = new Scope(this);
scopes[i].position = i;
//handleResize();
}
scopes[i].setElm(menuElm);
if (i > 0)
scopes[i].speed = scopes[i-1].speed;
}
if (menu=="scopepop") {
pushUndo();
if (item=="remove")
scopes[menuScope].setElm(null);
if (item=="removeplot")
scopes[menuScope].removePlot(menuPlot);
if (item=="speed2")
scopes[menuScope].speedUp();
if (item=="speed1/2")
scopes[menuScope].slowDown();
// if (item=="scale")
// scopes[menuScope].adjustScale(.5);
if (item=="maxscale")
scopes[menuScope].maxScale();
if (item=="stack")
stackScope(menuScope);
if (item=="unstack")
unstackScope(menuScope);
if (item=="combine")
combineScope(menuScope);
if (item=="selecty")
scopes[menuScope].selectY();
if (item=="reset")
scopes[menuScope].resetGraph();
if (item.indexOf("show")==0 || item=="plotxy" || item=="showfft") {
scopes[menuScope].handleMenu(item);
}
//cv.repaint();
}
if (menu=="circuits" && item.indexOf("setup ") ==0) {
pushUndo();
int sp = item.indexOf(' ', 6);
readSetupFile(item.substring(6, sp), item.substring(sp+1), true);
}
// if (ac.indexOf("setup ") == 0) {
// pushUndo();
// readSetupFile(ac.substring(6),
// ((MenuItem) e.getSource()).getLabel());
// }
// IES: Moved from itemStateChanged()
if (menu=="main") {
if (contextPanel!=null)
contextPanel.hide();
// MenuItem mmi = (MenuItem) mi;
// int prevMouseMode = mouseMode;
setMouseMode(MODE_ADD_ELM);
String s = item;
if (s.length() > 0)
mouseModeStr = s;
if (s.compareTo("DragAll") == 0)
setMouseMode(MODE_DRAG_ALL);
else if (s.compareTo("DragRow") == 0)
setMouseMode(MODE_DRAG_ROW);
else if (s.compareTo("DragColumn") == 0)
setMouseMode(MODE_DRAG_COLUMN);
else if (s.compareTo("DragSelected") == 0)
setMouseMode(MODE_DRAG_SELECTED);
else if (s.compareTo("DragPost") == 0)
setMouseMode(MODE_DRAG_POST);
else if (s.compareTo("Select") == 0)
setMouseMode(MODE_SELECT);
// else if (s.length() > 0) {
// try {
// addingClass = Class.forName(s);
// } catch (Exception ee) {
// ee.printStackTrace();
// }
// }
// else
// setMouseMode(prevMouseMode);
tempMouseMode = mouseMode;
}
}
void stackScope(int s) {
if (s == 0) {
if (scopeCount < 2)
return;
s = 1;
}
if (scopes[s].position == scopes[s-1].position)
return;
scopes[s].position = scopes[s-1].position;
for (s++; s < scopeCount; s++)
scopes[s].position--;
}
void unstackScope(int s) {
if (s == 0) {
if (scopeCount < 2)
return;
s = 1;
}
if (scopes[s].position != scopes[s-1].position)
return;
for (; s < scopeCount; s++)
scopes[s].position++;
}
void combineScope(int s) {
if (s == 0) {
if (scopeCount < 2)
return;
s = 1;
}
scopes[s-1].combine(scopes[s]);
scopes[s].setElm(null);
}
void stackAll() {
int i;
for (i = 0; i != scopeCount; i++) {
scopes[i].position = 0;
scopes[i].showMax = scopes[i].showMin = false;
}
}
void unstackAll() {
int i;
for (i = 0; i != scopeCount; i++) {
scopes[i].position = i;
scopes[i].showMax = true;
}
}
void combineAll() {
int i;
for (i = scopeCount-2; i >= 0; i--) {
scopes[i].combine(scopes[i+1]);
scopes[i+1].setElm(null);
}
}
void doEdit(Editable eable) {
clearSelection();
pushUndo();
if (editDialog != null) {
// requestFocus();
editDialog.setVisible(false);
editDialog = null;
}
editDialog = new EditDialog(eable, this);
editDialog.show();
}
void doExportAsUrl()
{
String dump = dumpCircuit();
// if (expDialog == null) {
// expDialog = ImportExportDialogFactory.Create(this,
// ImportExportDialog.Action.EXPORT);
//// expDialog = new ImportExportClipboardDialog(this,
//// ImportExportDialog.Action.EXPORT);
// }
// expDialog.setDump(dump);
// expDialog.execute();
exportAsUrlDialog = new ExportAsUrlDialog(dump);
exportAsUrlDialog.show();
}
void doExportAsText()
{
String dump = dumpCircuit();
exportAsTextDialog = new ExportAsTextDialog(this, dump);
exportAsTextDialog.show();
}
void doExportAsLocalFile() {
String dump = dumpCircuit();
exportAsLocalFileDialog = new ExportAsLocalFileDialog(dump);
exportAsLocalFileDialog.show();
}
void doExportToDropbox() {
String dump = dumpCircuit();
exportToDropbox = new ExportToDropbox(dump);
}
String dumpCircuit() {
int i;
CustomLogicModel.clearDumpedFlags();
int f = (dotsCheckItem.getState()) ? 1 : 0;
f |= (smallGridCheckItem.getState()) ? 2 : 0;
f |= (voltsCheckItem.getState()) ? 0 : 4;
f |= (powerCheckItem.getState()) ? 8 : 0;
f |= (showValuesCheckItem.getState()) ? 0 : 16;
// 32 = linear scale in afilter
String dump = "$ " + f + " " +
timeStep + " " + getIterCount() + " " +
currentBar.getValue() + " " + CircuitElm.voltageRange + " " +
powerBar.getValue() + "\n";
for (i = 0; i != elmList.size(); i++) {
CircuitElm ce = getElm(i);
if (ce instanceof CustomLogicElm) {
String m = ((CustomLogicElm)ce).dumpModel();
if (!m.isEmpty())
dump += m + "\n";
}
dump += ce.dump() + "\n";
}
for (i = 0; i != scopeCount; i++) {
String d = scopes[i].dump();
if (d != null)
dump += d + "\n";
}
if (hintType != -1)
dump += "h " + hintType + " " + hintItem1 + " " +
hintItem2 + "\n";
return dump;
}
void getSetupList(final boolean openDefault) {
String url;
url = GWT.getModuleBaseURL()+"setuplist.txt"+"?v="+random.nextInt();
RequestBuilder requestBuilder = new RequestBuilder(RequestBuilder.GET, url);
try {
requestBuilder.sendRequest(null, new RequestCallback() {
public void onError(Request request, Throwable exception) {
GWT.log("File Error Response", exception);
}
public void onResponseReceived(Request request, Response response) {
// processing goes here
if (response.getStatusCode()==Response.SC_OK) {
String text = response.getText();
processSetupList(text.getBytes(), text.length(), openDefault);
// end or processing
}
else
GWT.log("Bad file server response:"+response.getStatusText() );
}
});
} catch (RequestException e) {
GWT.log("failed file reading", e);
}
}
void processSetupList(byte b[], int len, final boolean openDefault) {
MenuBar currentMenuBar;
MenuBar stack[] = new MenuBar[6];
int stackptr = 0;
currentMenuBar=new MenuBar(true);
currentMenuBar.setAutoOpen(true);
menuBar.addItem(LS("Circuits"), currentMenuBar);
stack[stackptr++] = currentMenuBar;
int p;
for (p = 0; p < len; ) {
int l;
for (l = 0; l != len-p; l++)
if (b[l+p] == '\n') {
l++;
break;
}
String line = new String(b, p, l-1);
if (line.charAt(0) == '#')
;
else if (line.charAt(0) == '+') {
// MenuBar n = new Menu(line.substring(1));
MenuBar n = new MenuBar(true);
n.setAutoOpen(true);
currentMenuBar.addItem(LS(line.substring(1)),n);
currentMenuBar = stack[stackptr++] = n;
} else if (line.charAt(0) == '-') {
currentMenuBar = stack[--stackptr-1];
} else {
int i = line.indexOf(' ');
if (i > 0) {
String title = LS(line.substring(i+1));
boolean first = false;
if (line.charAt(0) == '>')
first = true;
String file = line.substring(first ? 1 : 0, i);
currentMenuBar.addItem(new MenuItem(title,
new MyCommand("circuits", "setup "+file+" " + title)));
if (file.equals(startCircuit) && startLabel == null) {
startLabel = title;
titleLabel.setText(title);
}
if (first && startCircuit == null) {
startCircuit = file;
startLabel = title;
if (openDefault && stopMessage == null)
readSetupFile(startCircuit, startLabel, true);
}
}
}
p += l;
}
}
void readSetup(String text, boolean centre) {
readSetup(text, false, centre);
titleLabel.setText(null);
}
void readSetup(String text, boolean retain, boolean centre) {
readSetup(text.getBytes(), text.length(), retain, centre);
titleLabel.setText(null);
}
void readSetupFile(String str, String title, boolean centre) {
t = 0;
System.out.println(str);
// TODO: Maybe think about some better approach to cache management!
String url=GWT.getModuleBaseURL()+"circuits/"+str+"?v="+random.nextInt();
loadFileFromURL(url, centre);
if (title != null)
titleLabel.setText(title);
}
void loadFileFromURL(String url, final boolean centre) {
RequestBuilder requestBuilder = new RequestBuilder(RequestBuilder.GET, url);
try {
requestBuilder.sendRequest(null, new RequestCallback() {
public void onError(Request request, Throwable exception) {
GWT.log("File Error Response", exception);
}
public void onResponseReceived(Request request, Response response) {
if (response.getStatusCode()==Response.SC_OK) {
String text = response.getText();
readSetup(text.getBytes(), text.length(), false, centre);
}
else
GWT.log("Bad file server response:"+response.getStatusText() );
}
});
} catch (RequestException e) {
GWT.log("failed file reading", e);
}
}
void readSetup(byte b[], int len, boolean retain, boolean centre) {
int i;
if (!retain) {
clearMouseElm();
for (i = 0; i != elmList.size(); i++) {
CircuitElm ce = getElm(i);
ce.delete();
}
elmList.removeAllElements();
hintType = -1;
timeStep = 5e-6;
dotsCheckItem.setState(false);
smallGridCheckItem.setState(false);
powerCheckItem.setState(false);
voltsCheckItem.setState(true);
showValuesCheckItem.setState(true);
setGrid();
speedBar.setValue(117); // 57
currentBar.setValue(50);
powerBar.setValue(50);
CircuitElm.voltageRange = 5;
scopeCount = 0;
lastIterTime = 0;
}
//cv.repaint();
int p;
for (p = 0; p < len; ) {
int l;
int linelen = len-p; // IES - changed to allow the last line to not end with a delim.
for (l = 0; l != len-p; l++)
if (b[l+p] == '\n' || b[l+p] == '\r') {
linelen = l++;
if (l+p < b.length && b[l+p] == '\n')
l++;
break;
}
String line = new String(b, p, linelen);
StringTokenizer st = new StringTokenizer(line, " +\t\n\r\f");
while (st.hasMoreTokens()) {
String type = st.nextToken();
int tint = type.charAt(0);
try {
if (tint == 'o') {
Scope sc = new Scope(this);
sc.position = scopeCount;
sc.undump(st);
scopes[scopeCount++] = sc;
break;
}
if (tint == 'h') {
readHint(st);
break;
}
if (tint == '$') {
readOptions(st);
break;
}
if (tint == '!') {
new CustomLogicModel(st);
break;
}
if (tint == '%' || tint == '?' || tint == 'B') {
// ignore afilter-specific stuff
break;
}
if (tint >= '0' && tint <= '9')
tint = new Integer(type).intValue();
int x1 = new Integer(st.nextToken()).intValue();
int y1 = new Integer(st.nextToken()).intValue();
int x2 = new Integer(st.nextToken()).intValue();
int y2 = new Integer(st.nextToken()).intValue();
int f = new Integer(st.nextToken()).intValue();
// The following lines are functionally replaced by the call to
// createCe below
// Class cls = dumpTypes[tint];
// if (cls == null) {
// System.out.println("unrecognized dump type: " + type);
// break;
// }
// // find element class
// Class carr[] = new Class[6];
// //carr[0] = getClass();
// carr[0] = carr[1] = carr[2] = carr[3] = carr[4] =
// int.class;
// carr[5] = StringTokenizer.class;
// Constructor cstr = null;
// cstr = cls.getConstructor(carr);
//
// // invoke constructor with starting coordinates
// Object oarr[] = new Object[6];
// //oarr[0] = this;
// oarr[0] = new Integer(x1);
// oarr[1] = new Integer(y1);
// oarr[2] = new Integer(x2);
// oarr[3] = new Integer(y2);
// oarr[4] = new Integer(f );
// oarr[5] = st;
// ce = (CircuitElm) cstr.newInstance(oarr);
CircuitElm newce = createCe(tint, x1, y1, x2, y2, f, st);
if (newce==null) {
System.out.println("unrecognized dump type: " + type);
break;
}
newce.setPoints();
elmList.addElement(newce);
// } catch (java.lang.reflect.InvocationTargetException ee) {
// ee.getTargetException().printStackTrace();
// break;
} catch (Exception ee) {
ee.printStackTrace();
console("exception while undumping " + ee);
break;
}
break;
}
p += l;
}
setPowerBarEnable();
enableItems();
// if (!retain)
// handleResize(); // for scopes
needAnalyze();
if (centre)
centreCircuit();
}
void readHint(StringTokenizer st) {
hintType = new Integer(st.nextToken()).intValue();
hintItem1 = new Integer(st.nextToken()).intValue();
hintItem2 = new Integer(st.nextToken()).intValue();
}
void readOptions(StringTokenizer st) {
int flags = new Integer(st.nextToken()).intValue();
// IES - remove inteaction
dotsCheckItem.setState((flags & 1) != 0);
smallGridCheckItem.setState((flags & 2) != 0);
voltsCheckItem.setState((flags & 4) == 0);
powerCheckItem.setState((flags & 8) == 8);
showValuesCheckItem.setState((flags & 16) == 0);
timeStep = new Double (st.nextToken()).doubleValue();
double sp = new Double(st.nextToken()).doubleValue();
int sp2 = (int) (Math.log(10*sp)*24+61.5);
//int sp2 = (int) (Math.log(sp)*24+1.5);
speedBar.setValue(sp2);
currentBar.setValue(new Integer(st.nextToken()).intValue());
CircuitElm.voltageRange = new Double (st.nextToken()).doubleValue();
try {
powerBar.setValue(new Integer(st.nextToken()).intValue());
} catch (Exception e) {
}
setGrid();
}
int snapGrid(int x) {
return (x+gridRound) & gridMask;
}
boolean doSwitch(int x, int y) {
if (mouseElm == null || !(mouseElm instanceof SwitchElm))
return false;
SwitchElm se = (SwitchElm) mouseElm;
if (!se.getSwitchRect().contains(x, y))
return false;
se.toggle();
if (se.momentary)
heldSwitchElm = se;
needAnalyze();
return true;
}
int locateElm(CircuitElm elm) {
int i;
for (i = 0; i != elmList.size(); i++)
if (elm == elmList.elementAt(i))
return i;
return -1;
}
public void mouseDragged(MouseMoveEvent e) {
// ignore right mouse button with no modifiers (needed on PC)
if (e.getNativeButton()==NativeEvent.BUTTON_RIGHT) {
if (!(e.isMetaKeyDown() ||
e.isShiftKeyDown() ||
e.isControlKeyDown() ||
e.isAltKeyDown()))
return;
}
if (tempMouseMode==MODE_DRAG_SPLITTER) {
dragSplitter(e.getX(), e.getY());
return;
}
int gx = inverseTransformX(e.getX());
int gy = inverseTransformY(e.getY());
if (!circuitArea.contains(e.getX(), e.getY()))
return;
if (dragElm != null)
dragElm.drag(gx, gy);
boolean success = true;
switch (tempMouseMode) {
case MODE_DRAG_ALL:
dragAll(e.getX(), e.getY());
break;
case MODE_DRAG_ROW:
dragRow(snapGrid(gx), snapGrid(gy));
break;
case MODE_DRAG_COLUMN:
dragColumn(snapGrid(gx), snapGrid(gy));
break;
case MODE_DRAG_POST:
if (mouseElm != null)
dragPost(snapGrid(gx), snapGrid(gy));
break;
case MODE_SELECT:
if (mouseElm == null)
selectArea(gx, gy);
else {
// wait short delay before dragging. This is to fix problem where switches were accidentally getting
// dragged when tapped on mobile devices
if (System.currentTimeMillis()-mouseDownTime < 150)
return;
tempMouseMode = MODE_DRAG_SELECTED;
success = dragSelected(gx, gy);
}
break;
case MODE_DRAG_SELECTED:
success = dragSelected(gx, gy);
break;
}
dragging = true;
if (success) {
dragScreenX = e.getX();
dragScreenY = e.getY();
// console("setting dragGridx in mousedragged");
dragGridX = inverseTransformX(dragScreenX);
dragGridY = inverseTransformY(dragScreenY);
if (!(tempMouseMode == MODE_DRAG_SELECTED && onlyGraphicsElmsSelected())) {
dragGridX = snapGrid(dragGridX);
dragGridY = snapGrid(dragGridY);
}
}
}
void dragSplitter(int x, int y) {
double h = (double) cv.getCanvasElement().getHeight();
if (h<1)
h=1;
scopeHeightFraction=1.0-(((double)y)/h);
if (scopeHeightFraction<0.1)
scopeHeightFraction=0.1;
if (scopeHeightFraction>0.9)
scopeHeightFraction=0.9;
setCircuitArea();
}
void dragAll(int x, int y) {
int dx = x-dragScreenX;
int dy = y-dragScreenY;
if (dx == 0 && dy == 0)
return;
transform[4] += dx;
transform[5] += dy;
dragScreenX = x;
dragScreenY = y;
}
void dragRow(int x, int y) {
int dy = y-dragGridY;
if (dy == 0)
return;
int i;
for (i = 0; i != elmList.size(); i++) {
CircuitElm ce = getElm(i);
if (ce.y == dragGridY)
ce.movePoint(0, 0, dy);
if (ce.y2 == dragGridY)
ce.movePoint(1, 0, dy);
}
removeZeroLengthElements();
}
void dragColumn(int x, int y) {
int dx = x-dragGridX;
if (dx == 0)
return;
int i;
for (i = 0; i != elmList.size(); i++) {
CircuitElm ce = getElm(i);
if (ce.x == dragGridX)
ce.movePoint(0, dx, 0);
if (ce.x2 == dragGridX)
ce.movePoint(1, dx, 0);
}
removeZeroLengthElements();
}
boolean onlyGraphicsElmsSelected() {
if (mouseElm!=null && !(mouseElm instanceof GraphicElm))
return false;
int i;
for (i = 0; i != elmList.size(); i++) {
CircuitElm ce = getElm(i);
if ( ce.isSelected() && !(ce instanceof GraphicElm) )
return false;
}
return true;
}
boolean dragSelected(int x, int y) {
boolean me = false;
int i;
if (mouseElm != null && !mouseElm.isSelected())
mouseElm.setSelected(me = true);
if (! onlyGraphicsElmsSelected()) {
// console("Snapping x and y");
x = snapGrid(x);
y = snapGrid(y);
}
int dx = x-dragGridX;
// console("dx="+dx+"dragGridx="+dragGridX);
int dy = y-dragGridY;
if (dx == 0 && dy == 0) {
// don't leave mouseElm selected if we selected it above
if (me)
mouseElm.setSelected(false);
return false;
}
boolean allowed = true;
// check if moves are allowed
for (i = 0; allowed && i != elmList.size(); i++) {
CircuitElm ce = getElm(i);
if (ce.isSelected() && !ce.allowMove(dx, dy))
allowed = false;
}
if (allowed) {
for (i = 0; i != elmList.size(); i++) {
CircuitElm ce = getElm(i);
if (ce.isSelected())
ce.move(dx, dy);
}
needAnalyze();
}
// don't leave mouseElm selected if we selected it above
if (me)
mouseElm.setSelected(false);
return allowed;
}
void dragPost(int x, int y) {
if (draggingPost == -1) {
draggingPost =
(Graphics.distanceSq(mouseElm.x , mouseElm.y , x, y) >
Graphics.distanceSq(mouseElm.x2, mouseElm.y2, x, y)) ? 1 : 0;
}
int dx = x-dragGridX;
int dy = y-dragGridY;
if (dx == 0 && dy == 0)
return;
mouseElm.movePoint(draggingPost, dx, dy);
needAnalyze();
}
void doFlip() {
mouseElm.flipPosts();
needAnalyze();
}
void selectArea(int x, int y) {
int x1 = min(x, initDragGridX);
int x2 = max(x, initDragGridX);
int y1 = min(y, initDragGridY);
int y2 = max(y, initDragGridY);
selectedArea = new Rectangle(x1, y1, x2-x1, y2-y1);
int i;
for (i = 0; i != elmList.size(); i++) {
CircuitElm ce = getElm(i);
ce.selectRect(selectedArea);
}
}
// void setSelectedElm(CircuitElm cs) {
// int i;
// for (i = 0; i != elmList.size(); i++) {
// CircuitElm ce = getElm(i);
// ce.setSelected(ce == cs);
// }
// mouseElm = cs;
// }
void setMouseElm(CircuitElm ce) {
if (ce!=mouseElm) {
if (mouseElm!=null)
mouseElm.setMouseElm(false);
if (ce!=null)
ce.setMouseElm(true);
mouseElm=ce;
}
}
void removeZeroLengthElements() {
int i;
boolean changed = false;
for (i = elmList.size()-1; i >= 0; i--) {
CircuitElm ce = getElm(i);
if (ce.x == ce.x2 && ce.y == ce.y2) {
elmList.removeElementAt(i);
ce.delete();
changed = true;
}
}
needAnalyze();
}
boolean mouseIsOverSplitter(int x, int y) {
boolean isOverSplitter;
isOverSplitter =((x>=0) && (x<circuitArea.width) &&
(y>=circuitArea.height-5) && (y<circuitArea.height));
if (isOverSplitter!=mouseWasOverSplitter){
if (isOverSplitter)
setCursorStyle("cursorSplitter");
else
setMouseMode(mouseMode);
}
mouseWasOverSplitter=isOverSplitter;
return isOverSplitter;
}
public void onMouseMove(MouseMoveEvent e) {
e.preventDefault();
mouseCursorX=e.getX();
mouseCursorY=e.getY();
if (mouseDragging) {
mouseDragged(e);
return;
}
mouseSelect(e);
}
// convert screen coordinates to grid coordinates by inverting circuit transform
int inverseTransformX(double x) {
return (int) ((x-transform[4])/transform[0]);
}
int inverseTransformY(double y) {
return (int) ((y-transform[5])/transform[3]);
}
// need to break this out into a separate routine to handle selection,
// since we don't get mouse move events on mobile
public void mouseSelect(MouseEvent<?> e) {
// The following is in the original, but seems not to work/be needed for GWT
// if (e.getNativeButton()==NativeEvent.BUTTON_LEFT)
// return;
CircuitElm newMouseElm=null;
mouseCursorX=e.getX();
mouseCursorY=e.getY();
int sx = e.getX();
int sy = e.getY();
int gx = inverseTransformX(sx);
int gy = inverseTransformY(sy);
// console("Settingd draggridx in mouseEvent");
dragGridX = snapGrid(gx);
dragGridY = snapGrid(gy);
dragScreenX = sx;
dragScreenY = sy;
draggingPost = -1;
int i;
// CircuitElm origMouse = mouseElm;
mousePost = -1;
plotXElm = plotYElm = null;
if (mouseIsOverSplitter(sx, sy)) {
setMouseElm(null);
return;
}
if (mouseElm!=null && ( mouseElm.getHandleGrabbedClose(gx, gy, POSTGRABSQ, MINPOSTGRABSIZE)>=0)) {
newMouseElm=mouseElm;
} else {
int bestDist = 100000000;
int bestArea = 100000000;
for (i = 0; i != elmList.size(); i++) {
CircuitElm ce = getElm(i);
if (ce.boundingBox.contains(gx, gy)) {
int j;
int area = ce.boundingBox.width * ce.boundingBox.height;
int jn = ce.getPostCount();
if (jn > 2)
jn = 2;
for (j = 0; j != jn; j++) {
Point pt = ce.getPost(j);
int dist = Graphics.distanceSq(gx, gy, pt.x, pt.y);
// if multiple elements have overlapping bounding boxes,
// we prefer selecting elements that have posts close
// to the mouse pointer and that have a small bounding
// box area.
if (dist <= bestDist && area <= bestArea) {
bestDist = dist;
bestArea = area;
newMouseElm = ce;
}
}
if (ce.getPostCount() == 0)
newMouseElm = ce;
}
} // for
}
scopeSelected = -1;
if (newMouseElm == null) {
for (i = 0; i != scopeCount; i++) {
Scope s = scopes[i];
if (s.rect.contains(sx, sy)) {
newMouseElm=s.getElm();
if (s.plotXY) {
plotXElm = s.getXElm();
plotYElm = s.getYElm();
}
scopeSelected = i;
}
}
// // the mouse pointer was not in any of the bounding boxes, but we
// // might still be close to a post
for (i = 0; i != elmList.size(); i++) {
CircuitElm ce = getElm(i);
if (mouseMode==MODE_DRAG_POST ) {
if (ce.getHandleGrabbedClose(gx, gy, POSTGRABSQ, 0)> 0)
{
newMouseElm = ce;
break;
}
}
int j;
int jn = ce.getPostCount();
for (j = 0; j != jn; j++) {
Point pt = ce.getPost(j);
// int dist = Graphics.distanceSq(x, y, pt.x, pt.y);
if (Graphics.distanceSq(pt.x, pt.y, gx, gy) < 26) {
newMouseElm = ce;
mousePost = j;
break;
}
}
}
} else {
mousePost = -1;
// look for post close to the mouse pointer
for (i = 0; i != newMouseElm.getPostCount(); i++) {
Point pt = newMouseElm.getPost(i);
if (Graphics.distanceSq(pt.x, pt.y, gx, gy) < 26)
mousePost = i;
}
}
// if (mouseElm != origMouse)
// cv.repaint();
setMouseElm(newMouseElm);
}
public void onContextMenu(ContextMenuEvent e) {
e.preventDefault();
menuX = e.getNativeEvent().getClientX();
menuY = e.getNativeEvent().getClientY();
doPopupMenu();
}
void doPopupMenu() {
menuElm = mouseElm;
menuScope=-1;
menuPlot=-1;
int x, y;
if (scopeSelected!=-1) {
MenuBar m=scopes[scopeSelected].getMenu();
menuScope=scopeSelected;
menuPlot=scopes[scopeSelected].selectedPlot;
if (m!=null) {
contextPanel=new PopupPanel(true);
contextPanel.add(m);
y=Math.max(0, Math.min(menuY,cv.getCoordinateSpaceHeight()-430));
contextPanel.setPopupPosition(menuX, y);
contextPanel.show();
}
} else if (mouseElm != null) {
elmScopeMenuItem.setEnabled(mouseElm.canViewInScope());
elmEditMenuItem .setEnabled(mouseElm.getEditInfo(0) != null);
elmFlipMenuItem .setEnabled(mouseElm.getPostCount() == 2);
contextPanel=new PopupPanel(true);
contextPanel.add(elmMenuBar);
contextPanel.setPopupPosition(menuX, menuY);
contextPanel.show();
} else {
doMainMenuChecks();
contextPanel=new PopupPanel(true);
contextPanel.add(mainMenuBar);
x=Math.max(0, Math.min(menuX, cv.getCoordinateSpaceWidth()-400));
y=Math.max(0, Math.min(menuY,cv.getCoordinateSpaceHeight()-450));
contextPanel.setPopupPosition(x,y);
contextPanel.show();
}
}
void longPress() {
doPopupMenu();
}
// public void mouseClicked(MouseEvent e) {
public void onClick(ClickEvent e) {
e.preventDefault();
// //IES - remove inteaction
//// if ( e.getClickCount() == 2 && !didSwitch )
//// doEditMenu(e);
// if (e.getNativeButton() == NativeEvent.BUTTON_LEFT) {
// if (mouseMode == MODE_SELECT || mouseMode == MODE_DRAG_SELECTED)
// clearSelection();
// }
if ((e.getNativeButton() == NativeEvent.BUTTON_MIDDLE))
scrollValues(e.getNativeEvent().getClientX(), e.getNativeEvent().getClientY(), 0);
}
public void onDoubleClick(DoubleClickEvent e){
e.preventDefault();
// if (!didSwitch && mouseElm != null)
if (mouseElm != null && !(mouseElm instanceof SwitchElm))
doEdit(mouseElm);
}
// public void mouseEntered(MouseEvent e) {
// }
public void onMouseOut(MouseOutEvent e) {
mouseCursorX=-1;
}
void clearMouseElm() {
scopeSelected = -1;
mouseElm = plotXElm = plotYElm = null;
}
int menuX, menuY;
public void onMouseDown(MouseDownEvent e) {
// public void mousePressed(MouseEvent e) {
e.preventDefault();
menuX = e.getX();
menuY = e.getY();
mouseDownTime = System.currentTimeMillis();
// maybe someone did copy in another window? should really do this when
// window receives focus
enablePaste();
// IES - hack to only handle left button events in the web version.
if (e.getNativeButton() != NativeEvent.BUTTON_LEFT)
return;
// set mouseElm in case we are on mobile
mouseSelect(e);
mouseDragging=true;
didSwitch = false;
if (mouseWasOverSplitter) {
tempMouseMode = MODE_DRAG_SPLITTER;
return;
}
if (e.getNativeButton() == NativeEvent.BUTTON_LEFT) {
// // left mouse
tempMouseMode = mouseMode;
if (e.isAltKeyDown() && e.isMetaKeyDown())
tempMouseMode = MODE_DRAG_COLUMN;
else if (e.isAltKeyDown() && e.isShiftKeyDown())
tempMouseMode = MODE_DRAG_ROW;
else if (e.isShiftKeyDown())
tempMouseMode = MODE_SELECT;
else if (e.isAltKeyDown())
tempMouseMode = MODE_DRAG_ALL;
else if (e.isControlKeyDown() || e.isMetaKeyDown())
tempMouseMode = MODE_DRAG_POST;
}
int gx = inverseTransformX(e.getX());
int gy = inverseTransformY(e.getY());
if (doSwitch(gx, gy)) {
// do this BEFORE we change the mouse mode to MODE_DRAG_POST! Or else logic inputs
// will add dots to the whole circuit when we click on them!
didSwitch = true;
return;
}
// IES - Grab resize handles in select mode if they are far enough apart and you are on top of them
if (tempMouseMode == MODE_SELECT && mouseElm!=null &&
mouseElm.getHandleGrabbedClose(gx, gy, POSTGRABSQ, MINPOSTGRABSIZE) >=0 &&
!anySelectedButMouse() )
tempMouseMode = MODE_DRAG_POST;
if (tempMouseMode != MODE_SELECT && tempMouseMode != MODE_DRAG_SELECTED)
clearSelection();
pushUndo();
initDragGridX = gx;
initDragGridY = gy;
dragging = true;
if (tempMouseMode !=MODE_ADD_ELM)
return;
//
int x0 = snapGrid(gx);
int y0 = snapGrid(gy);
if (!circuitArea.contains(e.getX(), e.getY()))
return;
dragElm = constructElement(mouseModeStr, x0, y0);
}
void doMainMenuChecks() {
int c = mainMenuItems.size();
int i;
for (i=0; i<c ; i++)
mainMenuItems.get(i).setState(mainMenuItemNames.get(i)==mouseModeStr);
}
public void onMouseUp(MouseUpEvent e) {
e.preventDefault();
mouseDragging=false;
// click to clear selection
if (tempMouseMode == MODE_SELECT && selectedArea == null)
clearSelection();
tempMouseMode = mouseMode;
selectedArea = null;
dragging = false;
boolean circuitChanged = false;
if (heldSwitchElm != null) {
heldSwitchElm.mouseUp();
heldSwitchElm = null;
circuitChanged = true;
}
if (dragElm != null) {
// if the element is zero size then don't create it
// IES - and disable any previous selection
if (dragElm.x == dragElm.x2 && dragElm.y == dragElm.y2) {
dragElm.delete();
if (mouseMode == MODE_SELECT || mouseMode == MODE_DRAG_SELECTED)
clearSelection();
}
else {
elmList.addElement(dragElm);
dragElm.draggingDone();
circuitChanged = true;
}
dragElm = null;
}
if (circuitChanged)
needAnalyze();
if (dragElm != null)
dragElm.delete();
dragElm = null;
// cv.repaint();
}
public void onMouseWheel(MouseWheelEvent e) {
e.preventDefault();
// once we start zooming, don't allow other uses of mouse wheel for a while
// so we don't accidentally edit a resistor value while zooming
boolean zoomOnly = System.currentTimeMillis() < zoomTime+1000;
if (!zoomOnly)
scrollValues(e.getNativeEvent().getClientX(), e.getNativeEvent().getClientY(), e.getDeltaY());
if (mouseElm instanceof MouseWheelHandler && !zoomOnly)
((MouseWheelHandler) mouseElm).onMouseWheel(e);
else if (scopeSelected != -1)
scopes[scopeSelected].onMouseWheel(e);
else if (!dialogIsShowing()) {
zoomCircuit(e.getDeltaY());
zoomTime = System.currentTimeMillis();
}
}
void zoomCircuit(int dy) {
double newScale;
double oldScale = transform[0];
double val = dy*.01;
newScale = Math.max(oldScale+val, .2);
newScale = Math.min(newScale, 2.5);
setCircuitScale(newScale);
}
void setCircuitScale(double newScale) {
int cx = inverseTransformX(circuitArea.width/2);
int cy = inverseTransformY(circuitArea.height/2);
transform[0] = transform[3] = newScale;
// adjust translation to keep center of screen constant
// inverse transform = (x-t4)/t0
transform[4] = circuitArea.width /2 - cx*newScale;
transform[5] = circuitArea.height/2 - cy*newScale;
}
void setPowerBarEnable() {
if (powerCheckItem.getState()) {
powerLabel.setStyleName("disabled", false);
powerBar.enable();
} else {
powerLabel.setStyleName("disabled", true);
powerBar.disable();
}
}
void scrollValues(int x, int y, int deltay) {
if (mouseElm!=null && !dialogIsShowing() && scopeSelected == -1)
if (mouseElm instanceof ResistorElm || mouseElm instanceof CapacitorElm || mouseElm instanceof InductorElm) {
scrollValuePopup = new ScrollValuePopup(x, y, deltay, mouseElm, this);
}
}
void enableItems() {
}
void setGrid() {
gridSize = (smallGridCheckItem.getState()) ? 8 : 16;
gridMask = ~(gridSize-1);
gridRound = gridSize/2-1;
}
void pushUndo() {
redoStack.removeAllElements();
String s = dumpCircuit();
if (undoStack.size() > 0 &&
s.compareTo(undoStack.lastElement()) == 0)
return;
undoStack.add(s);
enableUndoRedo();
}
void doUndo() {
if (undoStack.size() == 0)
return;
redoStack.add(dumpCircuit());
String s = undoStack.remove(undoStack.size()-1);
readSetup(s, false);
enableUndoRedo();
}
void doRedo() {
if (redoStack.size() == 0)
return;
undoStack.add(dumpCircuit());
String s = redoStack.remove(redoStack.size()-1);
readSetup(s, false);
enableUndoRedo();
}
void enableUndoRedo() {
redoItem.setEnabled(redoStack.size() > 0);
undoItem.setEnabled(undoStack.size() > 0);
}
void setMouseMode(int mode)
{
mouseMode = mode;
if ( mode == MODE_ADD_ELM ) {
setCursorStyle("cursorCross");
} else {
setCursorStyle("cursorPointer");
}
}
void setCursorStyle(String s) {
if (lastCursorStyle!=null)
cv.removeStyleName(lastCursorStyle);
cv.addStyleName(s);
lastCursorStyle=s;
}
void setMenuSelection() {
if (menuElm != null) {
if (menuElm.selected)
return;
clearSelection();
menuElm.setSelected(true);
}
}
void doCut() {
int i;
pushUndo();
setMenuSelection();
clipboard = "";
for (i = elmList.size()-1; i >= 0; i--) {
CircuitElm ce = getElm(i);
if (ce.isSelected()) {
clipboard += ce.dump() + "\n";
ce.delete();
elmList.removeElementAt(i);
}
}
writeClipboardToStorage();
enablePaste();
needAnalyze();
}
void writeClipboardToStorage() {
Storage stor = Storage.getLocalStorageIfSupported();
if (stor == null)
return;
stor.setItem("circuitClipboard", clipboard);
}
void readClipboardFromStorage() {
Storage stor = Storage.getLocalStorageIfSupported();
if (stor == null)
return;
clipboard = stor.getItem("circuitClipboard");
}
void doDelete() {
int i;
pushUndo();
setMenuSelection();
boolean hasDeleted = false;
for (i = elmList.size()-1; i >= 0; i--) {
CircuitElm ce = getElm(i);
if (ce.isSelected()) {
ce.delete();
elmList.removeElementAt(i);
hasDeleted = true;
}
}
if ( !hasDeleted )
{
for (i = elmList.size()-1; i >= 0; i--) {
CircuitElm ce = getElm(i);
if (ce == mouseElm) {
ce.delete();
elmList.removeElementAt(i);
hasDeleted = true;
setMouseElm(null);
break;
}
}
}
if ( hasDeleted )
needAnalyze();
}
void doCopy() {
int i;
clipboard = "";
setMenuSelection();
for (i = elmList.size()-1; i >= 0; i--) {
CircuitElm ce = getElm(i);
if (ce.isSelected())
clipboard += ce.dump() + "\n";
}
writeClipboardToStorage();
enablePaste();
}
void enablePaste() {
if (clipboard == null || clipboard.length() == 0)
readClipboardFromStorage();
pasteItem.setEnabled(clipboard != null && clipboard.length() > 0);
}
void doDuplicate() {
int i;
String s = "";
setMenuSelection();
for (i = elmList.size()-1; i >= 0; i--) {
CircuitElm ce = getElm(i);
if (ce.isSelected())
s += ce.dump() + "\n";
}
doPaste(s);
}
void doPaste(String dump) {
pushUndo();
clearSelection();
int i;
Rectangle oldbb = null;
for (i = 0; i != elmList.size(); i++) {
CircuitElm ce = getElm(i);
Rectangle bb = ce.getBoundingBox();
if (oldbb != null)
oldbb = oldbb.union(bb);
else
oldbb = bb;
}
int oldsz = elmList.size();
if (dump != null)
readSetup(dump, true, false);
else {
readClipboardFromStorage();
readSetup(clipboard, true, false);
}
// select new items
Rectangle newbb = null;
for (i = oldsz; i != elmList.size(); i++) {
CircuitElm ce = getElm(i);
ce.setSelected(true);
Rectangle bb = ce.getBoundingBox();
if (newbb != null)
newbb = newbb.union(bb);
else
newbb = bb;
}
if (oldbb != null && newbb != null && oldbb.intersects(newbb)) {
// find a place for new items
int dx = 0, dy = 0;
int spacew = circuitArea.width - oldbb.width - newbb.width;
int spaceh = circuitArea.height - oldbb.height - newbb.height;
if (spacew > spaceh)
dx = snapGrid(oldbb.x + oldbb.width - newbb.x + gridSize);
else
dy = snapGrid(oldbb.y + oldbb.height - newbb.y + gridSize);
for (i = oldsz; i != elmList.size(); i++) {
CircuitElm ce = getElm(i);
ce.move(dx, dy);
}
// center circuit
// handleResize();
}
needAnalyze();
}
void clearSelection() {
int i;
for (i = 0; i != elmList.size(); i++) {
CircuitElm ce = getElm(i);
ce.setSelected(false);
}
}
void doSelectAll() {
int i;
for (i = 0; i != elmList.size(); i++) {
CircuitElm ce = getElm(i);
ce.setSelected(true);
}
}
boolean anySelectedButMouse() {
for (int i=0; i != elmList.size(); i++)
if (getElm(i)!= mouseElm && getElm(i).selected)
return true;
return false;
}
// public void keyPressed(KeyEvent e) {}
// public void keyReleased(KeyEvent e) {}
boolean dialogIsShowing() {
if (editDialog!=null && editDialog.isShowing())
return true;
if (customLogicEditDialog!=null && customLogicEditDialog.isShowing())
return true;
if (exportAsUrlDialog != null && exportAsUrlDialog.isShowing())
return true;
if (exportAsTextDialog != null && exportAsTextDialog.isShowing())
return true;
if (exportAsLocalFileDialog != null && exportAsLocalFileDialog.isShowing())
return true;
if (contextPanel!=null && contextPanel.isShowing())
return true;
if (scrollValuePopup != null && scrollValuePopup.isShowing())
return true;
if (aboutBox !=null && aboutBox.isShowing())
return true;
if (importFromTextDialog !=null && importFromTextDialog.isShowing())
return true;
if (importFromDropboxDialog != null && importFromDropboxDialog.isShowing())
return true;
return false;
}
public void onPreviewNativeEvent(NativePreviewEvent e) {
int cc=e.getNativeEvent().getCharCode();
int t=e.getTypeInt();
int code=e.getNativeEvent().getKeyCode();
if (dialogIsShowing()) {
if (scrollValuePopup != null && scrollValuePopup.isShowing() &&
(t & Event.ONKEYDOWN)!=0) {
if (code==KEY_ESCAPE || code==KEY_SPACE)
scrollValuePopup.close(false);
if (code==KEY_ENTER)
scrollValuePopup.close(true);
}
if (editDialog!=null && editDialog.isShowing() &&
(t & Event.ONKEYDOWN)!=0) {
if (code==KEY_ESCAPE)
editDialog.closeDialog();
if (code==KEY_ENTER) {
editDialog.apply();
editDialog.closeDialog();
}
}
return;
}
if ((t & Event.ONKEYDOWN)!=0) {
if (code==KEY_BACKSPACE || code==KEY_DELETE) {
doDelete();
e.cancel();
}
if (code==KEY_ESCAPE){
setMouseMode(MODE_SELECT);
mouseModeStr = "Select";
tempMouseMode = mouseMode;
e.cancel();
}
if (e.getNativeEvent().getCtrlKey() || e.getNativeEvent().getMetaKey()) {
if (code==KEY_C) {
menuPerformed("key", "copy");
e.cancel();
}
if (code==KEY_X) {
menuPerformed("key", "cut");
e.cancel();
}
if (code==KEY_V) {
menuPerformed("key", "paste");
e.cancel();
}
if (code==KEY_Z) {
menuPerformed("key", "undo");
e.cancel();
}
if (code==KEY_Y) {
menuPerformed("key", "redo");
e.cancel();
}
if (code==KEY_D) {
menuPerformed("key", "duplicate");
e.cancel();
}
if (code==KEY_A) {
menuPerformed("key", "selectAll");
e.cancel();
}
}
}
if ((t&Event.ONKEYPRESS)!=0) {
if (cc=='-') {
menuPerformed("key", "zoomout");
e.cancel();
}
if (cc=='+' || cc == '=') {
menuPerformed("key", "zoomin");
e.cancel();
}
if (cc=='0') {
menuPerformed("key", "zoom100");
e.cancel();
}
if (cc>32 && cc<127){
String c=shortcuts[cc];
e.cancel();
if (c==null)
return;
setMouseMode(MODE_ADD_ELM);
mouseModeStr=c;
tempMouseMode = mouseMode;
}
if (cc==32) {
setMouseMode(MODE_SELECT);
mouseModeStr = "Select";
tempMouseMode = mouseMode;
e.cancel();
}
}
}
// factors a matrix into upper and lower triangular matrices by
// gaussian elimination. On entry, a[0..n-1][0..n-1] is the
// matrix to be factored. ipvt[] returns an integer vector of pivot
// indices, used in the lu_solve() routine.
boolean lu_factor(double a[][], int n, int ipvt[]) {
double scaleFactors[];
int i,j,k;
scaleFactors = new double[n];
// divide each row by its largest element, keeping track of the
// scaling factors
for (i = 0; i != n; i++) {
double largest = 0;
for (j = 0; j != n; j++) {
double x = Math.abs(a[i][j]);
if (x > largest)
largest = x;
}
// if all zeros, it's a singular matrix
if (largest == 0)
return false;
scaleFactors[i] = 1.0/largest;
}
// use Crout's method; loop through the columns
for (j = 0; j != n; j++) {
// calculate upper triangular elements for this column
for (i = 0; i != j; i++) {
double q = a[i][j];
for (k = 0; k != i; k++)
q -= a[i][k]*a[k][j];
a[i][j] = q;
}
// calculate lower triangular elements for this column
double largest = 0;
int largestRow = -1;
for (i = j; i != n; i++) {
double q = a[i][j];
for (k = 0; k != j; k++)
q -= a[i][k]*a[k][j];
a[i][j] = q;
double x = Math.abs(q);
if (x >= largest) {
largest = x;
largestRow = i;
}
}
// pivoting
if (j != largestRow) {
double x;
for (k = 0; k != n; k++) {
x = a[largestRow][k];
a[largestRow][k] = a[j][k];
a[j][k] = x;
}
scaleFactors[largestRow] = scaleFactors[j];
}
// keep track of row interchanges
ipvt[j] = largestRow;
// avoid zeros
if (a[j][j] == 0.0) {
System.out.println("avoided zero");
a[j][j]=1e-18;
}
if (j != n-1) {
double mult = 1.0/a[j][j];
for (i = j+1; i != n; i++)
a[i][j] *= mult;
}
}
return true;
}
// Solves the set of n linear equations using a LU factorization
// previously performed by lu_factor. On input, b[0..n-1] is the right
// hand side of the equations, and on output, contains the solution.
void lu_solve(double a[][], int n, int ipvt[], double b[]) {
int i;
// find first nonzero b element
for (i = 0; i != n; i++) {
int row = ipvt[i];
double swap = b[row];
b[row] = b[i];
b[i] = swap;
if (swap != 0)
break;
}
int bi = i++;
for (; i < n; i++) {
int row = ipvt[i];
int j;
double tot = b[row];
b[row] = b[i];
// forward substitution using the lower triangular matrix
for (j = bi; j < i; j++)
tot -= a[i][j]*b[j];
b[i] = tot;
}
for (i = n-1; i >= 0; i--) {
double tot = b[i];
// back-substitution using the upper triangular matrix
int j;
for (j = i+1; j != n; j++)
tot -= a[i][j]*b[j];
b[i] = tot/a[i][i];
}
}
void createNewLoadFile() {
// This is a hack to fix what IMHO is a bug in the <INPUT FILE element
// reloading the same file doesn't create a change event so importing the same file twice
// doesn't work unless you destroy the original input element and replace it with a new one
int idx=verticalPanel.getWidgetIndex(loadFileInput);
LoadFile newlf=new LoadFile(this);
verticalPanel.insert(newlf, idx);
verticalPanel.remove(idx+1);
loadFileInput=newlf;
}
void addWidgetToVerticalPanel(Widget w) {
if (iFrame!=null) {
int i=verticalPanel.getWidgetIndex(iFrame);
verticalPanel.insert(w, i);
setiFrameHeight();
}
else
verticalPanel.add(w);
}
void removeWidgetFromVerticalPanel(Widget w){
verticalPanel.remove(w);
if (iFrame!=null)
setiFrameHeight();
}
public static CircuitElm createCe(int tint, int x1, int y1, int x2, int y2, int f, StringTokenizer st) {
if (tint=='g')
return (CircuitElm) new GroundElm(x1, y1, x2, y2, f, st);
if (tint=='r')
return (CircuitElm) new ResistorElm(x1, y1, x2, y2, f, st);
if (tint=='R')
return (CircuitElm) new RailElm(x1, y1, x2, y2, f, st);
if (tint=='s')
return (CircuitElm) new SwitchElm(x1, y1, x2, y2, f, st);
if (tint=='S')
return (CircuitElm) new Switch2Elm(x1, y1, x2, y2, f, st);
if (tint=='t')
return (CircuitElm) new TransistorElm(x1, y1, x2, y2, f, st);
if (tint=='w')
return (CircuitElm) new WireElm(x1, y1, x2, y2, f, st);
if (tint=='c')
return (CircuitElm) new CapacitorElm(x1, y1, x2, y2, f, st);
if (tint==209)
return (CircuitElm) new PolarCapacitorElm(x1, y1, x2, y2, f, st);
if (tint=='l')
return (CircuitElm) new InductorElm(x1, y1, x2, y2, f, st);
if (tint=='v')
return (CircuitElm) new VoltageElm(x1, y1, x2, y2, f, st);
if (tint==172)
return (CircuitElm) new VarRailElm(x1, y1, x2, y2, f, st);
if (tint==174)
return (CircuitElm) new PotElm(x1, y1, x2, y2, f, st);
if (tint=='O')
return (CircuitElm) new OutputElm(x1, y1, x2, y2, f, st);
if (tint=='i')
return (CircuitElm) new CurrentElm(x1, y1, x2, y2, f, st);
if (tint=='p')
return (CircuitElm) new ProbeElm(x1, y1, x2, y2, f, st);
if (tint=='d')
return (CircuitElm) new DiodeElm(x1, y1, x2, y2, f, st);
if (tint=='z')
return (CircuitElm) new ZenerElm(x1, y1, x2, y2, f, st);
if (tint==170)
return (CircuitElm) new SweepElm(x1, y1, x2, y2, f, st);
if (tint==162)
return (CircuitElm) new LEDElm(x1, y1, x2, y2, f, st);
if (tint=='A')
return (CircuitElm) new AntennaElm(x1, y1, x2, y2, f, st);
if (tint=='L')
return (CircuitElm) new LogicInputElm(x1, y1, x2, y2, f, st);
if (tint=='M')
return (CircuitElm) new LogicOutputElm(x1, y1, x2, y2, f, st);
if (tint=='T')
return (CircuitElm) new TransformerElm(x1, y1, x2, y2, f, st);
if (tint==169)
return (CircuitElm) new TappedTransformerElm(x1, y1, x2, y2, f, st);
if (tint==171)
return (CircuitElm) new TransLineElm(x1, y1, x2, y2, f, st);
if (tint==178)
return (CircuitElm) new RelayElm(x1, y1, x2, y2, f, st);
if (tint=='m')
return (CircuitElm) new MemristorElm(x1, y1, x2, y2, f, st);
if (tint==187)
return (CircuitElm) new SparkGapElm(x1, y1, x2, y2, f, st);
if (tint==200)
return (CircuitElm) new AMElm(x1, y1, x2, y2, f, st);
if (tint==201)
return (CircuitElm) new FMElm(x1, y1, x2, y2, f, st);
if (tint=='n')
return (CircuitElm) new NoiseElm(x1, y1, x2, y2, f, st);
if (tint==181)
return (CircuitElm) new LampElm(x1, y1, x2, y2, f, st);
if (tint=='a')
return (CircuitElm) new OpAmpElm(x1, y1, x2, y2, f, st);
if (tint=='f')
return (CircuitElm) new MosfetElm(x1, y1, x2, y2, f, st);
if (tint=='j')
return (CircuitElm) new JfetElm(x1, y1, x2, y2, f, st);
if (tint==159)
return (CircuitElm) new AnalogSwitchElm(x1, y1, x2, y2, f, st);
if (tint==160)
return (CircuitElm) new AnalogSwitch2Elm(x1, y1, x2, y2, f, st);
if (tint==180)
return (CircuitElm) new TriStateElm(x1, y1, x2, y2, f, st);
if (tint==182)
return (CircuitElm) new SchmittElm(x1, y1, x2, y2, f, st);
if (tint==183)
return (CircuitElm) new InvertingSchmittElm(x1, y1, x2, y2, f, st);
if (tint==177)
return (CircuitElm) new SCRElm(x1, y1, x2, y2, f, st);
if (tint==203)
return (CircuitElm) new DiacElm(x1, y1, x2, y2, f, st);
if (tint==206)
return (CircuitElm) new TriacElm(x1, y1, x2, y2, f, st);
if (tint==173)
return (CircuitElm) new TriodeElm(x1, y1, x2, y2, f, st);
if (tint==175)
return (CircuitElm) new TunnelDiodeElm(x1, y1, x2, y2, f, st);
if (tint==179)
return (CircuitElm) new CC2Elm(x1, y1, x2, y2, f, st);
if (tint=='I')
return (CircuitElm) new InverterElm(x1, y1, x2, y2, f, st);
if (tint==151)
return (CircuitElm) new NandGateElm(x1, y1, x2, y2, f, st);
if (tint==153)
return (CircuitElm) new NorGateElm(x1, y1, x2, y2, f, st);
if (tint==150)
return (CircuitElm) new AndGateElm(x1, y1, x2, y2, f, st);
if (tint==152)
return (CircuitElm) new OrGateElm(x1, y1, x2, y2, f, st);
if (tint==154)
return (CircuitElm) new XorGateElm(x1, y1, x2, y2, f, st);
if (tint==155)
return (CircuitElm) new DFlipFlopElm(x1, y1, x2, y2, f, st);
if (tint==156)
return (CircuitElm) new JKFlipFlopElm(x1, y1, x2, y2, f, st);
if (tint==157)
return (CircuitElm) new SevenSegElm(x1, y1, x2, y2, f, st);
if (tint==184)
return (CircuitElm) new MultiplexerElm(x1, y1, x2, y2, f, st);
if (tint==185)
return (CircuitElm) new DeMultiplexerElm(x1, y1, x2, y2, f, st);
if (tint==189)
return (CircuitElm) new SipoShiftElm(x1, y1, x2, y2, f, st);
if (tint==186)
return (CircuitElm) new PisoShiftElm(x1, y1, x2, y2, f, st);
if (tint==161)
return (CircuitElm) new PhaseCompElm(x1, y1, x2, y2, f, st);
if (tint==164)
return (CircuitElm) new CounterElm(x1, y1, x2, y2, f, st);
if (tint==163)
return (CircuitElm) new DecadeElm(x1, y1, x2, y2, f, st);
if (tint==165)
return (CircuitElm) new TimerElm(x1, y1, x2, y2, f, st);
if (tint==166)
return (CircuitElm) new DACElm(x1, y1, x2, y2, f, st);
if (tint==167)
return (CircuitElm) new ADCElm(x1, y1, x2, y2, f, st);
if (tint==168)
return (CircuitElm) new LatchElm(x1, y1, x2, y2, f, st);
if (tint==188)
return (CircuitElm) new SeqGenElm(x1, y1, x2, y2, f, st);
if (tint==158)
return (CircuitElm) new VCOElm(x1, y1, x2, y2, f, st);
if (tint=='b')
return (CircuitElm) new BoxElm(x1, y1, x2, y2, f, st);
if (tint=='x')
return (CircuitElm) new TextElm(x1, y1, x2, y2, f, st);
if (tint==193)
return (CircuitElm) new TFlipFlopElm(x1, y1, x2, y2, f, st);
if (tint==197)
return (CircuitElm) new SevenSegDecoderElm(x1, y1, x2, y2, f, st);
if (tint==196)
return (CircuitElm) new FullAdderElm(x1, y1, x2, y2, f, st);
if (tint==195)
return (CircuitElm) new HalfAdderElm(x1, y1, x2, y2, f, st);
if (tint==194)
return (CircuitElm) new MonostableElm(x1, y1, x2, y2, f, st);
if (tint==207)
return (CircuitElm) new LabeledNodeElm(x1, y1, x2, y2, f, st);
if (tint==208)
return (CircuitElm) new CustomLogicElm(x1, y1, x2, y2, f, st);
if (tint==210)
return (CircuitElm) new DataRecorderElm(x1, y1, x2, y2, f, st);
if (tint==211)
return (CircuitElm) new AudioOutputElm(x1, y1, x2, y2, f, st);
if (tint==212)
return (CircuitElm) new VCVSElm(x1, y1, x2, y2, f, st);
if (tint==213)
return (CircuitElm) new VCCSElm(x1, y1, x2, y2, f, st);
if (tint==214)
return (CircuitElm) new CCVSElm(x1, y1, x2, y2, f, st);
if (tint==215)
return (CircuitElm) new CCCSElm(x1, y1, x2, y2, f, st);
if (tint==368)
return new TestPointElm(x1, y1, x2, y2, f, st);
if (tint==370)
return new AmmeterElm(x1, y1, x2, y2, f, st);
if (tint==400)
return new DarlingtonElm(x1, y1, x2, y2, f, st);
if (tint==401)
return new ComparatorElm(x1, y1, x2, y2, f, st);
if (tint==402)
return new OTAElm(x1, y1, x2, y2, f, st);
return
null;
}
public static CircuitElm constructElement(String n, int x1, int y1){
if (n=="GroundElm")
return (CircuitElm) new GroundElm(x1, y1);
if (n=="ResistorElm")
return (CircuitElm) new ResistorElm(x1, y1);
if (n=="RailElm")
return (CircuitElm) new RailElm(x1, y1);
if (n=="SwitchElm")
return (CircuitElm) new SwitchElm(x1, y1);
if (n=="Switch2Elm")
return (CircuitElm) new Switch2Elm(x1, y1);
if (n=="NTransistorElm")
return (CircuitElm) new NTransistorElm(x1, y1);
if (n=="PTransistorElm")
return (CircuitElm) new PTransistorElm(x1, y1);
if (n=="WireElm")
return (CircuitElm) new WireElm(x1, y1);
if (n=="CapacitorElm")
return (CircuitElm) new CapacitorElm(x1, y1);
if (n=="PolarCapacitorElm")
return (CircuitElm) new PolarCapacitorElm(x1, y1);
if (n=="InductorElm")
return (CircuitElm) new InductorElm(x1, y1);
if (n=="DCVoltageElm")
return (CircuitElm) new DCVoltageElm(x1, y1);
if (n=="VarRailElm")
return (CircuitElm) new VarRailElm(x1, y1);
if (n=="PotElm")
return (CircuitElm) new PotElm(x1, y1);
if (n=="OutputElm")
return (CircuitElm) new OutputElm(x1, y1);
if (n=="CurrentElm")
return (CircuitElm) new CurrentElm(x1, y1);
if (n=="ProbeElm")
return (CircuitElm) new ProbeElm(x1, y1);
if (n=="DiodeElm")
return (CircuitElm) new DiodeElm(x1, y1);
if (n=="ZenerElm")
return (CircuitElm) new ZenerElm(x1, y1);
if (n=="ACVoltageElm")
return (CircuitElm) new ACVoltageElm(x1, y1);
if (n=="ACRailElm")
return (CircuitElm) new ACRailElm(x1, y1);
if (n=="SquareRailElm")
return (CircuitElm) new SquareRailElm(x1, y1);
if (n=="SweepElm")
return (CircuitElm) new SweepElm(x1, y1);
if (n=="LEDElm")
return (CircuitElm) new LEDElm(x1, y1);
if (n=="AntennaElm")
return (CircuitElm) new AntennaElm(x1, y1);
if (n=="LogicInputElm")
return (CircuitElm) new LogicInputElm(x1, y1);
if (n=="LogicOutputElm")
return (CircuitElm) new LogicOutputElm(x1, y1);
if (n=="TransformerElm")
return (CircuitElm) new TransformerElm(x1, y1);
if (n=="TappedTransformerElm")
return (CircuitElm) new TappedTransformerElm(x1, y1);
if (n=="TransLineElm")
return (CircuitElm) new TransLineElm(x1, y1);
if (n=="RelayElm")
return (CircuitElm) new RelayElm(x1, y1);
if (n=="MemristorElm")
return (CircuitElm) new MemristorElm(x1, y1);
if (n=="SparkGapElm")
return (CircuitElm) new SparkGapElm(x1, y1);
if (n=="ClockElm")
return (CircuitElm) new ClockElm(x1, y1);
if (n=="AMElm")
return (CircuitElm) new AMElm(x1, y1);
if (n=="FMElm")
return (CircuitElm) new FMElm(x1, y1);
if (n=="LampElm")
return (CircuitElm) new LampElm(x1, y1);
if (n=="PushSwitchElm")
return (CircuitElm) new PushSwitchElm(x1, y1);
if (n=="OpAmpElm")
return (CircuitElm) new OpAmpElm(x1, y1);
if (n=="OpAmpSwapElm")
return (CircuitElm) new OpAmpSwapElm(x1, y1);
if (n=="NMosfetElm")
return (CircuitElm) new NMosfetElm(x1, y1);
if (n=="PMosfetElm")
return (CircuitElm) new PMosfetElm(x1, y1);
if (n=="NJfetElm")
return (CircuitElm) new NJfetElm(x1, y1);
if (n=="PJfetElm")
return (CircuitElm) new PJfetElm(x1, y1);
if (n=="AnalogSwitchElm")
return (CircuitElm) new AnalogSwitchElm(x1, y1);
if (n=="AnalogSwitch2Elm")
return (CircuitElm) new AnalogSwitch2Elm(x1, y1);
if (n=="SchmittElm")
return (CircuitElm) new SchmittElm(x1, y1);
if (n=="InvertingSchmittElm")
return (CircuitElm) new InvertingSchmittElm(x1, y1);
if (n=="TriStateElm")
return (CircuitElm) new TriStateElm(x1, y1);
if (n=="SCRElm")
return (CircuitElm) new SCRElm(x1, y1);
if (n=="DiacElm")
return (CircuitElm) new DiacElm(x1, y1);
if (n=="TriacElm")
return (CircuitElm) new TriacElm(x1, y1);
if (n=="TriodeElm")
return (CircuitElm) new TriodeElm(x1, y1);
if (n=="TunnelDiodeElm")
return (CircuitElm) new TunnelDiodeElm(x1, y1);
if (n=="CC2Elm")
return (CircuitElm) new CC2Elm(x1, y1);
if (n=="CC2NegElm")
return (CircuitElm) new CC2NegElm(x1, y1);
if (n=="InverterElm")
return (CircuitElm) new InverterElm(x1, y1);
if (n=="NandGateElm")
return (CircuitElm) new NandGateElm(x1, y1);
if (n=="NorGateElm")
return (CircuitElm) new NorGateElm(x1, y1);
if (n=="AndGateElm")
return (CircuitElm) new AndGateElm(x1, y1);
if (n=="OrGateElm")
return (CircuitElm) new OrGateElm(x1, y1);
if (n=="XorGateElm")
return (CircuitElm) new XorGateElm(x1, y1);
if (n=="DFlipFlopElm")
return (CircuitElm) new DFlipFlopElm(x1, y1);
if (n=="JKFlipFlopElm")
return (CircuitElm) new JKFlipFlopElm(x1, y1);
if (n=="SevenSegElm")
return (CircuitElm) new SevenSegElm(x1, y1);
if (n=="MultiplexerElm")
return (CircuitElm) new MultiplexerElm(x1, y1);
if (n=="DeMultiplexerElm")
return (CircuitElm) new DeMultiplexerElm(x1, y1);
if (n=="SipoShiftElm")
return (CircuitElm) new SipoShiftElm(x1, y1);
if (n=="PisoShiftElm")
return (CircuitElm) new PisoShiftElm(x1, y1);
if (n=="PhaseCompElm")
return (CircuitElm) new PhaseCompElm(x1, y1);
if (n=="CounterElm")
return (CircuitElm) new CounterElm(x1, y1);
if (n=="DecadeElm")
return (CircuitElm) new DecadeElm(x1, y1);
if (n=="TimerElm")
return (CircuitElm) new TimerElm(x1, y1);
if (n=="DACElm")
return (CircuitElm) new DACElm(x1, y1);
if (n=="ADCElm")
return (CircuitElm) new ADCElm(x1, y1);
if (n=="LatchElm")
return (CircuitElm) new LatchElm(x1, y1);
if (n=="SeqGenElm")
return (CircuitElm) new SeqGenElm(x1, y1);
if (n=="VCOElm")
return (CircuitElm) new VCOElm(x1, y1);
if (n=="BoxElm")
return (CircuitElm) new BoxElm(x1, y1);
if (n=="TextElm")
return (CircuitElm) new TextElm(x1, y1);
if (n=="TFlipFlopElm")
return (CircuitElm) new TFlipFlopElm(x1, y1);
if (n=="SevenSegDecoderElm")
return (CircuitElm) new SevenSegDecoderElm(x1, y1);
if (n=="FullAdderElm")
return (CircuitElm) new FullAdderElm(x1, y1);
if (n=="HalfAdderElm")
return (CircuitElm) new HalfAdderElm(x1, y1);
if (n=="MonostableElm")
return (CircuitElm) new MonostableElm(x1, y1);
if (n=="LabeledNodeElm")
return (CircuitElm) new LabeledNodeElm(x1, y1);
if (n=="UserDefinedLogicElm")
return (CircuitElm) new CustomLogicElm(x1, y1);
if (n=="TestPointElm")
return new TestPointElm(x1, y1);
if (n=="AmmeterElm")
return new AmmeterElm(x1, y1);
if (n=="DataRecorderElm")
return (CircuitElm) new DataRecorderElm(x1, y1);
if (n=="AudioOutputElm")
return (CircuitElm) new AudioOutputElm(x1, y1);
if (n=="NDarlingtonElm")
return (CircuitElm) new NDarlingtonElm(x1, y1);
if (n=="PDarlingtonElm")
return (CircuitElm) new PDarlingtonElm(x1, y1);
if (n=="ComparatorElm")
return (CircuitElm) new ComparatorElm(x1, y1);
if (n=="OTAElm")
return (CircuitElm) new OTAElm(x1, y1);
if (n=="NoiseElm")
return (CircuitElm) new NoiseElm(x1, y1);
if (n=="VCVSElm")
return (CircuitElm) new VCVSElm(x1, y1);
if (n=="VCCSElm")
return (CircuitElm) new VCCSElm(x1, y1);
if (n=="CCVSElm")
return (CircuitElm) new CCVSElm(x1, y1);
if (n=="CCCSElm")
return (CircuitElm) new CCCSElm(x1, y1);
return null;
}
public void updateModels() {
int i;
for (i = 0; i != elmList.size(); i++)
elmList.get(i).updateModels();
}
native boolean weAreInUS() /*-{
try {
l = navigator.languages ? navigator.languages[0] : (navigator.language || navigator.userLanguage) ;
if (l.length > 2) {
l = l.slice(-2).toUpperCase();
return (l == "US" || l=="CA");
} else {
return 0;
}
} catch (e) { return 0;
}
}-*/;
static String LS(String s) {
String sm = localizationMap.get(s);
return sm != null ? sm : s;
}
static SafeHtml LSHTML(String s) { return SafeHtmlUtils.fromTrustedString(LS(s)); }
// For debugging
void dumpNodelist() {
CircuitNode nd;
CircuitElm e;
int i,j;
String s;
String cs;
//
// for(i=0; i<nodeList.size(); i++) {
// s="Node "+i;
// nd=nodeList.get(i);
// for(j=0; j<nd.links.size();j++) {
// s=s+" " + nd.links.get(j).num + " " +nd.links.get(j).elm.getDumpType();
// }
// console(s);
// }
console("Elm list Dump");
for (i=0;i<elmList.size(); i++) {
e=elmList.get(i);
cs = e.getDumpClass().toString();
int p = cs.lastIndexOf('.');
cs = cs.substring(p+1);
if (cs=="WireElm")
continue;
if (cs=="LabeledNodeElm")
cs = cs+" "+((LabeledNodeElm)e).text;
if (cs=="TransistorElm") {
if (((TransistorElm)e).pnp == -1)
cs= "PTransistorElm";
else
cs = "NTransistorElm";
}
s=cs;
for(j=0; j<e.getPostCount(); j++) {
s=s+" "+e.nodes[j];
}
console(s);
}
}
}